mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-80866 inline Android layout refactoring
This commit is contained in:
+4
@@ -62,6 +62,7 @@ import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.maven.AndroidMavenUtil;
|
||||
import org.jetbrains.android.refactoring.AndroidExtractAsIncludeAction;
|
||||
import org.jetbrains.android.refactoring.AndroidExtractStyleAction;
|
||||
import org.jetbrains.android.refactoring.AndroidInlineIncludeAction;
|
||||
import org.jetbrains.android.refactoring.AndroidInlineStyleReferenceAction;
|
||||
import org.jetbrains.android.sdk.AndroidPlatform;
|
||||
import org.jetbrains.android.sdk.AndroidSdkUtils;
|
||||
@@ -171,6 +172,9 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
|
||||
|
||||
action = manager.getAction(AndroidExtractAsIncludeAction.ACTION_ID);
|
||||
group.add(new AndroidRefactoringActionWrapper("E_xtract Layout", action));
|
||||
|
||||
action = manager.getAction(AndroidInlineIncludeAction.ACTION_ID);
|
||||
group.add(new AndroidRefactoringActionWrapper("I_nline Layout", action));
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
@@ -426,4 +426,8 @@ android.inline.style.title=Inline Android Style
|
||||
android.inline.style.command.name=Inline Style ''{0}''
|
||||
android.inline.style.inline.all.text=Inline &all references and remove the style
|
||||
android.inline.style.inline.this.text=Inline &this usage and keep the style
|
||||
android.extract.as.include.title=Extract Android Layout
|
||||
android.extract.as.include.title=Extract Android Layout
|
||||
android.inline.layout.command.name=Inline Layout File ''{0}''
|
||||
android.inline.file.inline.all.text=Inline &all references and remove the file
|
||||
android.inline.file.inline.this.text=Inline &this usage and keep the file
|
||||
android.inline.layout.title=Inline Android Layout
|
||||
@@ -94,6 +94,10 @@
|
||||
text="Inline _Style..." description="Inlines Android style">
|
||||
<add-to-group group-id="RefactoringMenu"/>
|
||||
</action>
|
||||
<action id="AndroidInlineIncludeAction" class="org.jetbrains.android.refactoring.AndroidInlineIncludeAction"
|
||||
text="Inline _Layout..." description="Inlines included Android layout">
|
||||
<add-to-group group-id="RefactoringMenu"/>
|
||||
</action>
|
||||
</actions>
|
||||
|
||||
<depends>JUnit</depends>
|
||||
@@ -247,7 +251,8 @@
|
||||
<compileServer.plugin classpath="jps/android-jps-plugin.jar;android-common.jar;android-rt.jar"/>
|
||||
<compileServer.plugin classpath="sdklib.jar;common.jar;jarutils.jar;androidprefs.jar"/>
|
||||
|
||||
<inlineActionHandler implementation="org.jetbrains.android.refactoring.AndroidInlineStyleHandler" order="last"/>
|
||||
<inlineActionHandler implementation="org.jetbrains.android.refactoring.AndroidInlineStyleHandler"/>
|
||||
<inlineActionHandler implementation="org.jetbrains.android.refactoring.AndroidInlineLayoutHandler"/>
|
||||
</extensions>
|
||||
|
||||
<extensionPoints>
|
||||
@@ -255,4 +260,4 @@
|
||||
<extensionPoint name="lightBuildProvider" interface="org.jetbrains.android.compiler.AndroidLightBuildProvider"/>
|
||||
<extensionPoint name="refactoringContextProvider" interface="org.jetbrains.android.refactoring.AndroidRefactoringContextProvider"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
</idea-plugin>
|
||||
+5
@@ -45,6 +45,11 @@ public class AndroidResourceReferenceBase extends PsiReferenceBase.Poly<XmlEleme
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ResourceValue getResourceValue() {
|
||||
return myResourceValue;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiElement[] computeTargetElements() {
|
||||
final ResolveResult[] resolveResults = multiResolve(false);
|
||||
|
||||
+5
-102
@@ -3,15 +3,8 @@ package org.jetbrains.android.refactoring;
|
||||
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.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiField;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
@@ -20,7 +13,6 @@ 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.OrderedSet;
|
||||
import org.jetbrains.android.dom.resources.ResourceNameConverter;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidResourceUtil;
|
||||
@@ -80,20 +72,14 @@ class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor {
|
||||
@Override
|
||||
protected UsageInfo[] findUsages() {
|
||||
final Set<UsageInfo> usages = new HashSet<UsageInfo>();
|
||||
addReferences(myStyleElement, usages);
|
||||
AndroidInlineUtil.addReferences(myStyleElement, usages);
|
||||
|
||||
for (PsiField field : AndroidResourceUtil.findResourceFieldsForValueResource(myStyleTag, false)) {
|
||||
addReferences(field, usages);
|
||||
AndroidInlineUtil.addReferences(field, usages);
|
||||
}
|
||||
return usages.toArray(new UsageInfo[usages.size()]);
|
||||
}
|
||||
|
||||
private static void addReferences(@NotNull PsiElement element, @NotNull Collection<UsageInfo> result) {
|
||||
for (PsiReference reference : ReferencesSearch.search(element)) {
|
||||
result.add(new UsageInfo(reference.getElement()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void performRefactoring(UsageInfo[] usages) {
|
||||
@@ -112,7 +98,7 @@ class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor {
|
||||
continue;
|
||||
}
|
||||
final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
|
||||
StyleUsageData usageData = tag != null ? AndroidInlineUtil.getUsageData(tag) : null;
|
||||
StyleUsageData usageData = tag != null ? AndroidInlineUtil.getStyleUsageData(tag) : null;
|
||||
|
||||
if (usageData == null) {
|
||||
if (usage.getReference() instanceof ResourceNameConverter.MyParentStyleReference) {
|
||||
@@ -135,7 +121,8 @@ class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor {
|
||||
unambiguousUsages.size() > 0 ||
|
||||
unsupportedUsages.size() > 0 ||
|
||||
implicitlyInherited.size() > 0) {
|
||||
final String errorMessage = buildErrorMessage(myProject, nonXmlUsages, unambiguousUsages, unsupportedUsages, implicitlyInherited);
|
||||
final String errorMessage = AndroidInlineUtil
|
||||
.buildErrorMessage(myProject, nonXmlUsages, unambiguousUsages, unsupportedUsages, implicitlyInherited);
|
||||
AndroidUtils.reportError(myProject, errorMessage, AndroidBundle.message("android.inline.style.title"));
|
||||
return;
|
||||
}
|
||||
@@ -156,88 +143,4 @@ class AndroidInlineAllStyleUsagesProcessor extends BaseRefactoringProcessor {
|
||||
// do it because the refactoring can be invoked from UI designer
|
||||
return UndoConfirmationPolicy.REQUEST_CONFIRMATION;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String buildErrorMessage(Project project,
|
||||
Collection<PsiElement> nonXmlUsages,
|
||||
Collection<PsiElement> unambiguousUsages,
|
||||
Collection<PsiElement> unsupportedUsages,
|
||||
Collection<PsiElement> implicitlyInherited) {
|
||||
final StringBuilder builder = new StringBuilder("Cannot perform refactoring\n\n");
|
||||
|
||||
if (nonXmlUsages.size() > 0) {
|
||||
builder.append("Non-XML references are not supported:\n");
|
||||
buildString(builder, project, nonXmlUsages);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
|
||||
if (unambiguousUsages.size() > 0) {
|
||||
builder.append("Unambiguous references:\n");
|
||||
buildString(builder, project, unambiguousUsages);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
|
||||
if (unsupportedUsages.size() > 0) {
|
||||
builder.append("Unsupported references:\n");
|
||||
buildString(builder, project, unsupportedUsages);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
|
||||
if (implicitlyInherited.size() > 0) {
|
||||
builder.append("Implicit inheritance is not supported:\n");
|
||||
buildString(builder, project, implicitlyInherited);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
builder.delete(builder.length() - 2, builder.length());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static void buildString(StringBuilder builder, Project project, Collection<PsiElement> invalidRefs) {
|
||||
final OrderedSet<String> lines = new OrderedSet<String>();
|
||||
|
||||
for (PsiElement usage : invalidRefs) {
|
||||
final PsiFile psiFile = usage.getContainingFile();
|
||||
final VirtualFile file = psiFile != null
|
||||
? psiFile.getVirtualFile()
|
||||
: null;
|
||||
if (file != null) {
|
||||
lines.add(" in '" + getPresentableFilePath(project, file) + "'");
|
||||
}
|
||||
else {
|
||||
lines.add(" in unknown file");
|
||||
}
|
||||
}
|
||||
|
||||
for (Iterator<String> it = lines.iterator(); it.hasNext(); ) {
|
||||
final String line = it.next();
|
||||
builder.append(line);
|
||||
|
||||
if (it.hasNext()) {
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPresentableFilePath(Project project, VirtualFile file) {
|
||||
final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project);
|
||||
final int contentRootCount = projectRootManager.getContentRoots().length;
|
||||
|
||||
if (contentRootCount == 0) {
|
||||
return file.getPath();
|
||||
}
|
||||
final VirtualFile contentRoot = projectRootManager.getFileIndex().getContentRootForFile(file);
|
||||
|
||||
if (contentRoot == null) {
|
||||
return file.getPath();
|
||||
}
|
||||
final String relativePath = VfsUtilCore.getRelativePath(file, contentRoot, '/');
|
||||
|
||||
if (relativePath == null) {
|
||||
return file.getPath();
|
||||
}
|
||||
final String presentableRelativePath = contentRootCount == 1
|
||||
? relativePath
|
||||
: contentRoot.getName() + '/' + relativePath;
|
||||
return FileUtil.toSystemDependentName(".../" + presentableRelativePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.jetbrains.android.refactoring;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.util.xml.DomManager;
|
||||
import org.jetbrains.android.dom.converters.AndroidResourceReferenceBase;
|
||||
import org.jetbrains.android.dom.layout.LayoutDomFileDescription;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidInlineIncludeAction extends AndroidBaseXmlRefactoringAction {
|
||||
private final AndroidInlineTestConfig myTestConfig;
|
||||
|
||||
public static final String ACTION_ID = "AndroidInlineIncludeAction";
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public AndroidInlineIncludeAction() {
|
||||
myTestConfig = null;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public AndroidInlineIncludeAction(AndroidInlineTestConfig testConfig) {
|
||||
myTestConfig = testConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRefactorForTags(@NotNull Project project, @NotNull final XmlTag[] tags) {
|
||||
assert tags.length == 1;
|
||||
final XmlTag tag = tags[0];
|
||||
|
||||
final PsiFile file = tag.getContainingFile();
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
final LayoutUsageData usageData = AndroidInlineUtil.getLayoutUsageData(tag);
|
||||
|
||||
if (usageData == null) {
|
||||
return;
|
||||
}
|
||||
final AndroidResourceReferenceBase reference = usageData.getReference();
|
||||
final String title = AndroidBundle.message("android.inline.layout.title");
|
||||
final PsiElement[] resolvedElements = reference.computeTargetElements();
|
||||
|
||||
if (resolvedElements.length == 0) {
|
||||
AndroidUtils.reportError(project, "Cannot find layout by reference '" + reference.getValue() + "'", title);
|
||||
return;
|
||||
}
|
||||
|
||||
if (resolvedElements.length > 1) {
|
||||
AndroidUtils.reportError(project, RefactoringBundle.getCannotRefactorMessage("Unambiguous layout reference."), title);
|
||||
return;
|
||||
}
|
||||
final PsiElement resolvedElement = resolvedElements[0];
|
||||
|
||||
if (!(resolvedElement instanceof XmlFile)) {
|
||||
AndroidUtils.reportError(project, "Cannot find layout by reference '" + reference.getValue() + "'", title);
|
||||
return;
|
||||
}
|
||||
AndroidInlineUtil.doInlineLayoutFile(project, (XmlFile)resolvedElement, usageData.getReference().getElement(), myTestConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForTags(@NotNull XmlTag[] tags) {
|
||||
return tags.length == 1 && AndroidInlineUtil.getLayoutUsageData(tags[0]) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyFile(PsiFile file) {
|
||||
return DomManager.getDomManager(file.getProject()).
|
||||
getDomFileDescription((XmlFile)file) instanceof LayoutDomFileDescription;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.jetbrains.android.refactoring;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.refactoring.inline.InlineOptionsDialog;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidInlineLayoutDialog extends InlineOptionsDialog {
|
||||
private final XmlFile myLayoutFile;
|
||||
private final XmlTag myLayoutRootTag;
|
||||
private final PsiElement myUsageElement;
|
||||
|
||||
public AndroidInlineLayoutDialog(@NotNull Project project,
|
||||
@NotNull XmlFile layoutFile,
|
||||
@NotNull XmlTag layoutRootTag,
|
||||
@Nullable PsiElement usageElement) {
|
||||
super(project, true, layoutFile);
|
||||
myLayoutFile = layoutFile;
|
||||
myLayoutRootTag = layoutRootTag;
|
||||
myUsageElement = usageElement;
|
||||
myInvokedOnReference = usageElement != null;
|
||||
setTitle(AndroidBundle.message("android.inline.layout.title"));
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNameLabelText() {
|
||||
return "Layout file '" + myLayoutFile.getName() + "'";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBorderTitle() {
|
||||
return "Inline";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInlineAllText() {
|
||||
return AndroidBundle.message("android.inline.file.inline.all.text");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInlineThisText() {
|
||||
return AndroidBundle.message("android.inline.file.inline.this.text");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isInlineThis() {
|
||||
return myUsageElement != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAction() {
|
||||
final PsiElement usageElement = isInlineThisOnly() ? myUsageElement : null;
|
||||
invokeRefactoring(new AndroidInlineLayoutProcessor(myProject, myLayoutFile, myLayoutRootTag, usageElement));
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package org.jetbrains.android.refactoring;
|
||||
|
||||
import com.intellij.codeInsight.TargetElementUtilBase;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.refactoring.InlineActionHandler;
|
||||
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.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.*;
|
||||
import com.intellij.util.xml.DomManager;
|
||||
import org.jetbrains.android.dom.converters.AndroidResourceReferenceBase;
|
||||
import org.jetbrains.android.dom.layout.LayoutDomFileDescription;
|
||||
import org.jetbrains.android.dom.wrappers.ResourceElementWrapper;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.ErrorReporter;
|
||||
import org.jetbrains.android.util.HintBasedErrorReporter;
|
||||
import org.jetbrains.android.util.ProjectBasedErrorReporter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidInlineLayoutHandler extends InlineActionHandler {
|
||||
private static AndroidInlineTestConfig ourTestConfig;
|
||||
|
||||
@TestOnly
|
||||
public static void setTestConfig(@Nullable AndroidInlineTestConfig testConfig) {
|
||||
ourTestConfig = testConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForLanguage(Language l) {
|
||||
return l == XMLLanguage.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInlineElement(PsiElement element) {
|
||||
if (element instanceof ResourceElementWrapper) {
|
||||
element = ((ResourceElementWrapper)element).getWrappee();
|
||||
}
|
||||
if (!(element instanceof XmlElement)) {
|
||||
return false;
|
||||
}
|
||||
if (AndroidFacet.getInstance(element) == null) {
|
||||
return false;
|
||||
}
|
||||
if (element instanceof XmlFile) {
|
||||
if (((XmlFile)element).getRootTag() == null) {
|
||||
return false;
|
||||
}
|
||||
return DomManager.getDomManager(element.getProject()).getDomFileDescription((XmlFile)element)
|
||||
instanceof LayoutDomFileDescription;
|
||||
}
|
||||
else if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_NAME) {
|
||||
return getLayoutUsageDataFromContext(element) != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LayoutUsageData getLayoutUsageDataFromContext(PsiElement context) {
|
||||
final XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class);
|
||||
return tag != null
|
||||
? AndroidInlineUtil.getLayoutUsageData(tag)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inlineElement(Project project, Editor editor, PsiElement element) {
|
||||
if (element instanceof ResourceElementWrapper) {
|
||||
element = ((ResourceElementWrapper)element).getWrappee();
|
||||
}
|
||||
|
||||
if (element instanceof XmlFile) {
|
||||
PsiElement usageElement = null;
|
||||
|
||||
if (editor != null) {
|
||||
final PsiReference reference = TargetElementUtilBase.findReference(editor);
|
||||
|
||||
if (reference != null) {
|
||||
usageElement = reference.getElement();
|
||||
}
|
||||
}
|
||||
AndroidInlineUtil.doInlineLayoutFile(project, (XmlFile)element, usageElement, ourTestConfig);
|
||||
return;
|
||||
}
|
||||
final LayoutUsageData usageData = getLayoutUsageDataFromContext(element);
|
||||
assert usageData != null;
|
||||
final AndroidResourceReferenceBase ref = usageData.getReference();
|
||||
final PsiElement[] elements = ref.computeTargetElements();
|
||||
final ErrorReporter errorReporter = editor != null
|
||||
? new HintBasedErrorReporter(editor)
|
||||
: new ProjectBasedErrorReporter(project);
|
||||
final String title = AndroidBundle.message("android.inline.layout.title");
|
||||
|
||||
if (elements.length == 0) {
|
||||
final String resName = ref.getResourceValue().getResourceName();
|
||||
final String message = resName != null
|
||||
? "Cannot find layout '" + resName + "'"
|
||||
: "Error: cannot find the layout";
|
||||
errorReporter.report(message, title);
|
||||
return;
|
||||
}
|
||||
|
||||
if (elements.length > 1) {
|
||||
errorReporter.report("Error: unambiguous reference", title);
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiElement resolvedElement = elements[0];
|
||||
if (!(resolvedElement instanceof XmlFile)) {
|
||||
errorReporter.report("Cannot inline reference '" + ref.getValue() + "'", title);
|
||||
return;
|
||||
}
|
||||
AndroidInlineUtil.doInlineLayoutFile(project, (XmlFile)resolvedElement, usageData.getIncludeTag(), ourTestConfig);
|
||||
}
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
package org.jetbrains.android.refactoring;
|
||||
|
||||
import com.intellij.lang.xml.XMLLanguage;
|
||||
import com.intellij.openapi.command.UndoConfirmationPolicy;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiField;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
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 org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidResourceUtil;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidInlineLayoutProcessor extends BaseRefactoringProcessor {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.refactoring.AndroidInlineLayoutProcessor");
|
||||
|
||||
private final XmlFile myLayoutFile;
|
||||
private final XmlTag myLayoutRootTag;
|
||||
private final PsiElement myUsageElement;
|
||||
|
||||
protected AndroidInlineLayoutProcessor(@NotNull Project project,
|
||||
@NotNull XmlFile file,
|
||||
@NotNull XmlTag rootTag,
|
||||
@Nullable PsiElement usageElement) {
|
||||
super(project);
|
||||
myLayoutFile = file;
|
||||
myLayoutRootTag = rootTag;
|
||||
myUsageElement = usageElement;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
|
||||
return new UsageViewDescriptorAdapter() {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement[] getElements() {
|
||||
return new PsiElement[]{myLayoutFile};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCodeReferencesText(int usagesCount, int filesCount) {
|
||||
return "References to be inlined" + UsageViewBundle.getReferencesString(usagesCount, filesCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProcessedElementsHeader() {
|
||||
return "Layout file to inline";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected UsageInfo[] findUsages() {
|
||||
if (myUsageElement != null) {
|
||||
return new UsageInfo[] {new UsageInfo(myUsageElement)};
|
||||
}
|
||||
final Set<UsageInfo> usages = new HashSet<UsageInfo>();
|
||||
AndroidInlineUtil.addReferences(myLayoutFile, usages);
|
||||
|
||||
for (PsiField field : AndroidResourceUtil.findResourceFieldsForFileResource(myLayoutFile, false)) {
|
||||
AndroidInlineUtil.addReferences(field, usages);
|
||||
}
|
||||
return usages.toArray(new UsageInfo[usages.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRefactoring(UsageInfo[] usages) {
|
||||
final List<LayoutUsageData> inlineInfos = new ArrayList<LayoutUsageData>();
|
||||
final List<PsiElement> nonXmlUsages = new ArrayList<PsiElement>();
|
||||
final List<PsiElement> unsupportedUsages = new ArrayList<PsiElement>();
|
||||
final List<PsiElement> unambiguousUsages = 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);
|
||||
final LayoutUsageData usageData = tag != null
|
||||
? AndroidInlineUtil.getLayoutUsageData(tag)
|
||||
: null;
|
||||
if (usageData == null) {
|
||||
unsupportedUsages.add(element);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (usageData.getReference().computeTargetElements().length > 1) {
|
||||
unambiguousUsages.add(element);
|
||||
continue;
|
||||
}
|
||||
inlineInfos.add(usageData);
|
||||
}
|
||||
|
||||
if (nonXmlUsages.size() > 0 || unambiguousUsages.size() > 0 || unsupportedUsages.size() > 0) {
|
||||
final String errorMessage = AndroidInlineUtil.buildErrorMessage(
|
||||
myProject, nonXmlUsages, unambiguousUsages, unsupportedUsages,
|
||||
Collections.<PsiElement>emptyList());
|
||||
AndroidUtils.reportError(myProject, errorMessage, AndroidBundle.message("android.inline.style.title"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (LayoutUsageData info : inlineInfos) {
|
||||
try {
|
||||
info.inline(myLayoutRootTag);
|
||||
}
|
||||
catch (AndroidRefactoringErrorException e) {
|
||||
LOG.info(e);
|
||||
String message = e.getMessage();
|
||||
|
||||
if (message == null) {
|
||||
message = "Refactoring was performed with errors";
|
||||
}
|
||||
AndroidUtils.reportError(myProject, message, AndroidBundle.message("android.inline.style.title"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (myUsageElement == null) {
|
||||
myLayoutFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCommandName() {
|
||||
return AndroidBundle.message("android.inline.layout.command.name", myLayoutFile.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UndoConfirmationPolicy getUndoConfirmationPolicy() {
|
||||
// do it because the refactoring can be invoked from UI designer
|
||||
return UndoConfirmationPolicy.REQUEST_CONFIRMATION;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -76,6 +76,6 @@ public class AndroidInlineStyleHandler extends InlineActionHandler {
|
||||
return null;
|
||||
}
|
||||
final XmlTag tag = PsiTreeUtil.getParentOfType(usageElement, XmlTag.class, false);
|
||||
return tag != null ? AndroidInlineUtil.getUsageData(tag) : null;
|
||||
return tag != null ? AndroidInlineUtil.getStyleUsageData(tag) : null;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ public class AndroidInlineStyleReferenceAction extends AndroidBaseXmlRefactoring
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
final StyleUsageData usageData = AndroidInlineUtil.getUsageData(tag);
|
||||
final StyleUsageData usageData = AndroidInlineUtil.getStyleUsageData(tag);
|
||||
|
||||
if (usageData == null) {
|
||||
return;
|
||||
@@ -87,7 +87,7 @@ public class AndroidInlineStyleReferenceAction extends AndroidBaseXmlRefactoring
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForTags(@NotNull XmlTag[] tags) {
|
||||
return tags.length == 1 && AndroidInlineUtil.getUsageData(tags[0]) != null;
|
||||
return tags.length == 1 && AndroidInlineUtil.getStyleUsageData(tags[0]) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,18 +5,27 @@ 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.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.XmlAttributeValue;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.OrderedSet;
|
||||
import com.intellij.util.xml.DomElement;
|
||||
import com.intellij.util.xml.DomManager;
|
||||
import com.intellij.util.xml.GenericAttributeValue;
|
||||
import org.jetbrains.android.dom.converters.AndroidResourceReferenceBase;
|
||||
import org.jetbrains.android.dom.layout.Include;
|
||||
import org.jetbrains.android.dom.layout.LayoutViewElement;
|
||||
import org.jetbrains.android.dom.resources.ResourceValue;
|
||||
import org.jetbrains.android.dom.resources.Style;
|
||||
@@ -28,6 +37,8 @@ import org.jetbrains.android.util.ErrorReporter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -59,7 +70,7 @@ class AndroidInlineUtil {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static StyleUsageData getUsageData(@NotNull XmlTag tag) {
|
||||
static StyleUsageData getStyleUsageData(@NotNull XmlTag tag) {
|
||||
final DomElement domElement = DomManager.getDomManager(tag.getProject()).getDomElement(tag);
|
||||
|
||||
if (domElement instanceof LayoutViewElement) {
|
||||
@@ -80,23 +91,39 @@ class AndroidInlineUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static LayoutUsageData getLayoutUsageData(@NotNull XmlTag tag) {
|
||||
final Project project = tag.getProject();
|
||||
final DomElement domElement = DomManager.getDomManager(project).getDomElement(tag);
|
||||
|
||||
if (domElement instanceof Include) {
|
||||
final GenericAttributeValue<ResourceValue> layoutAttribute = ((Include)domElement).getLayout();
|
||||
final AndroidResourceReferenceBase reference = getAndroidResourceReference(layoutAttribute);
|
||||
|
||||
if (reference != null) {
|
||||
return new LayoutUsageData(project, tag, reference);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static AndroidResourceReferenceBase getAndroidResourceReference(@Nullable GenericAttributeValue<ResourceValue> attribute) {
|
||||
if (attribute == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ResourceValue styleValue = attribute.getValue();
|
||||
if (styleValue == null || styleValue.getPackage() != null) {
|
||||
final ResourceValue resValue = attribute.getValue();
|
||||
if (resValue == null || resValue.getPackage() != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final XmlAttributeValue styleAttributeValue = attribute.getXmlAttributeValue();
|
||||
if (styleAttributeValue == null) {
|
||||
final XmlAttributeValue value = attribute.getXmlAttributeValue();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (PsiReference reference : styleAttributeValue.getReferences()) {
|
||||
for (PsiReference reference : value.getReferences()) {
|
||||
if (reference instanceof AndroidResourceReferenceBase) {
|
||||
return (AndroidResourceReferenceBase)reference;
|
||||
}
|
||||
@@ -237,6 +264,115 @@ class AndroidInlineUtil {
|
||||
? data : null;
|
||||
}
|
||||
|
||||
static void addReferences(@NotNull PsiElement element, @NotNull Collection<UsageInfo> result) {
|
||||
for (PsiReference reference : ReferencesSearch.search(element)) {
|
||||
result.add(new UsageInfo(reference.getElement()));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static String buildErrorMessage(Project project,
|
||||
Collection<PsiElement> nonXmlUsages,
|
||||
Collection<PsiElement> unambiguousUsages,
|
||||
Collection<PsiElement> unsupportedUsages,
|
||||
Collection<PsiElement> implicitlyInherited) {
|
||||
final StringBuilder builder = new StringBuilder("Cannot perform refactoring\n\n");
|
||||
|
||||
if (nonXmlUsages.size() > 0) {
|
||||
builder.append("Non-XML references are not supported:\n");
|
||||
buildString(builder, project, nonXmlUsages);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
|
||||
if (unambiguousUsages.size() > 0) {
|
||||
builder.append("Unambiguous references:\n");
|
||||
buildString(builder, project, unambiguousUsages);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
|
||||
if (unsupportedUsages.size() > 0) {
|
||||
builder.append("Unsupported references:\n");
|
||||
buildString(builder, project, unsupportedUsages);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
|
||||
if (implicitlyInherited.size() > 0) {
|
||||
builder.append("Implicit inheritance is not supported:\n");
|
||||
buildString(builder, project, implicitlyInherited);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
builder.delete(builder.length() - 2, builder.length());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static void buildString(StringBuilder builder, Project project, Collection<PsiElement> invalidRefs) {
|
||||
final OrderedSet<String> lines = new OrderedSet<String>();
|
||||
|
||||
for (PsiElement usage : invalidRefs) {
|
||||
final PsiFile psiFile = usage.getContainingFile();
|
||||
final VirtualFile file = psiFile != null
|
||||
? psiFile.getVirtualFile()
|
||||
: null;
|
||||
if (file != null) {
|
||||
lines.add(" in '" + getPresentableFilePath(project, file) + "'");
|
||||
}
|
||||
else {
|
||||
lines.add(" in unknown file");
|
||||
}
|
||||
}
|
||||
|
||||
for (Iterator<String> it = lines.iterator(); it.hasNext(); ) {
|
||||
final String line = it.next();
|
||||
builder.append(line);
|
||||
|
||||
if (it.hasNext()) {
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPresentableFilePath(Project project, VirtualFile file) {
|
||||
final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project);
|
||||
final int contentRootCount = projectRootManager.getContentRoots().length;
|
||||
|
||||
if (contentRootCount == 0) {
|
||||
return file.getPath();
|
||||
}
|
||||
final VirtualFile contentRoot = projectRootManager.getFileIndex().getContentRootForFile(file);
|
||||
|
||||
if (contentRoot == null) {
|
||||
return file.getPath();
|
||||
}
|
||||
final String relativePath = VfsUtilCore.getRelativePath(file, contentRoot, '/');
|
||||
|
||||
if (relativePath == null) {
|
||||
return file.getPath();
|
||||
}
|
||||
final String presentableRelativePath = contentRootCount == 1
|
||||
? relativePath
|
||||
: contentRoot.getName() + '/' + relativePath;
|
||||
return FileUtil.toSystemDependentName(".../" + presentableRelativePath);
|
||||
}
|
||||
|
||||
static void doInlineLayoutFile(@NotNull Project project,
|
||||
@NotNull XmlFile layoutFile,
|
||||
@Nullable PsiElement usageElement,
|
||||
@Nullable AndroidInlineTestConfig testConfig) {
|
||||
final XmlTag rootTag = layoutFile.getRootTag();
|
||||
assert rootTag != null;
|
||||
|
||||
if (testConfig == null) {
|
||||
final AndroidInlineLayoutDialog dialog = new AndroidInlineLayoutDialog(project, layoutFile, rootTag, usageElement);
|
||||
dialog.show();
|
||||
}
|
||||
else {
|
||||
final AndroidInlineLayoutProcessor processor =
|
||||
new AndroidInlineLayoutProcessor(project, layoutFile, rootTag, testConfig.isInlineThisOnly() ? usageElement : null);
|
||||
processor.setPreviewUsages(false);
|
||||
processor.run();
|
||||
}
|
||||
}
|
||||
|
||||
static class MyStyleData {
|
||||
private final String myStyleName;
|
||||
private final Style myStyleElement;
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package org.jetbrains.android.refactoring;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidRefactoringErrorException extends Exception {
|
||||
public AndroidRefactoringErrorException() {
|
||||
}
|
||||
|
||||
public AndroidRefactoringErrorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package org.jetbrains.android.refactoring;
|
||||
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.XmlElementFactory;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.xml.XmlAttribute;
|
||||
import com.intellij.psi.xml.XmlChildRole;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.android.dom.converters.AndroidResourceReferenceBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class LayoutUsageData {
|
||||
private final Project myProject;
|
||||
private final XmlTag myIncludeTag;
|
||||
private final AndroidResourceReferenceBase myReference;
|
||||
|
||||
public LayoutUsageData(@NotNull Project project,
|
||||
@NotNull XmlTag includeTag,
|
||||
@NotNull AndroidResourceReferenceBase reference) {
|
||||
myProject = project;
|
||||
myIncludeTag = includeTag;
|
||||
myReference = reference;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AndroidResourceReferenceBase getReference() {
|
||||
return myReference;
|
||||
}
|
||||
|
||||
public void inline(@NotNull XmlTag layoutRootTag) throws AndroidRefactoringErrorException {
|
||||
final XmlTag parent = myIncludeTag.getParentTag();
|
||||
|
||||
if ("merge".equals(layoutRootTag.getName()) && parent != null) {
|
||||
inlineMultiTags(myIncludeTag, parent, layoutRootTag, myProject);
|
||||
}
|
||||
else {
|
||||
inlineSingleTag(myIncludeTag, parent, layoutRootTag);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public XmlTag getIncludeTag() {
|
||||
return myIncludeTag;
|
||||
}
|
||||
|
||||
private static void inlineSingleTag(XmlTag includeTag, XmlTag includeParentTag, XmlTag layoutRootTag) {
|
||||
final Map<String, String> attributesToAdd = new HashMap<String, String>();
|
||||
|
||||
for (XmlAttribute attribute : includeTag.getAttributes()) {
|
||||
final String namespace = attribute.getNamespace();
|
||||
|
||||
if (SdkConstants.NS_RESOURCES.equals(namespace)) {
|
||||
attributesToAdd.put(attribute.getLocalName(), attribute.getValue());
|
||||
}
|
||||
}
|
||||
final XmlTag newTag = (XmlTag)includeTag.replace(layoutRootTag.copy());
|
||||
final List<XmlAttribute> toDelete = new ArrayList<XmlAttribute>();
|
||||
|
||||
for (XmlAttribute attribute : newTag.getAttributes()) {
|
||||
if (attribute.isNamespaceDeclaration()) {
|
||||
final String localName = attribute.getLocalName();
|
||||
final String prefix = localName.equals(attribute.getName()) ? "" : localName;
|
||||
final String namespace = attribute.getValue();
|
||||
|
||||
if (includeParentTag != null && namespace.equals(includeParentTag.getNamespaceByPrefix(prefix))) {
|
||||
toDelete.add(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (XmlAttribute attribute : toDelete) {
|
||||
attribute.delete();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : attributesToAdd.entrySet()) {
|
||||
final String localName = entry.getKey();
|
||||
final String value = entry.getValue();
|
||||
newTag.setAttribute(localName, SdkConstants.NS_RESOURCES, value);
|
||||
}
|
||||
CodeStyleManager.getInstance(newTag.getManager()).reformat(newTag);
|
||||
}
|
||||
|
||||
private static void inlineMultiTags(XmlTag includeTag, XmlTag includeTagParent, XmlTag mergeTag, Project project)
|
||||
throws AndroidRefactoringErrorException {
|
||||
final Map<String, String> namespacesFromParent = includeTagParent.getLocalNamespaceDeclarations();
|
||||
final Map<String, String> namespacesToAddToParent = new HashMap<String, String>();
|
||||
final Map<String, String> namespacesToAddToEachTag = new HashMap<String, String>();
|
||||
|
||||
for (Map.Entry<String, String> entry : mergeTag.getLocalNamespaceDeclarations().entrySet()) {
|
||||
final String prefix = entry.getKey();
|
||||
final String namespace = entry.getValue();
|
||||
final String declaredNamespace = namespacesFromParent.get(prefix);
|
||||
|
||||
if (declaredNamespace != null && !declaredNamespace.equals(namespace)) {
|
||||
namespacesToAddToEachTag.put(prefix, namespace);
|
||||
}
|
||||
else {
|
||||
namespacesToAddToParent.put(prefix, namespace);
|
||||
}
|
||||
}
|
||||
final XmlTag mergeTagCopy = (XmlTag)mergeTag.copy();
|
||||
final XmlElementFactory xmlElementFactory = XmlElementFactory.getInstance(project);
|
||||
|
||||
for (XmlTag subtag : mergeTagCopy.getSubTags()) {
|
||||
final XmlAttribute[] attributes = subtag.getAttributes();
|
||||
final XmlAttribute firstAttribute = attributes.length > 0 ? attributes[0] : null;
|
||||
|
||||
for (Map.Entry<String, String> entry : namespacesToAddToEachTag.entrySet()) {
|
||||
final String prefix = entry.getKey();
|
||||
final String namespace = entry.getValue();
|
||||
|
||||
if (!subtag.getLocalNamespaceDeclarations().containsKey(prefix)) {
|
||||
final XmlAttribute xmlnsAttr = xmlElementFactory.createXmlAttribute("xmlns:" + prefix, namespace);
|
||||
|
||||
if (firstAttribute != null) {
|
||||
subtag.addBefore(xmlnsAttr, firstAttribute);
|
||||
}
|
||||
else {
|
||||
subtag.add(xmlnsAttr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
replaceByTagContent(project, includeTag, mergeTagCopy);
|
||||
addNamespaceAttributes(includeTagParent, namespacesToAddToParent, project);
|
||||
}
|
||||
|
||||
private static void addNamespaceAttributes(XmlTag tag, Map<String, String> namespaces, Project project) {
|
||||
final XmlAttribute[] parentAttributes = tag.getAttributes();
|
||||
final XmlAttribute firstParentAttribute = parentAttributes.length > 0 ? parentAttributes[0] : null;
|
||||
final XmlElementFactory factory = XmlElementFactory.getInstance(project);
|
||||
|
||||
for (Map.Entry<String, String> entry : namespaces.entrySet()) {
|
||||
final String prefix = entry.getKey();
|
||||
final String namespace = entry.getValue();
|
||||
|
||||
if (!namespace.equals(tag.getNamespaceByPrefix(prefix))) {
|
||||
final XmlAttribute xmlnsAttr = factory.createXmlAttribute("xmlns:" + prefix, namespace);
|
||||
|
||||
if (firstParentAttribute != null) {
|
||||
tag.addBefore(xmlnsAttr, firstParentAttribute);
|
||||
}
|
||||
else {
|
||||
tag.add(xmlnsAttr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void replaceByTagContent(Project project, XmlTag tagToReplace, XmlTag tagToInline)
|
||||
throws AndroidRefactoringErrorException {
|
||||
final ASTNode node = tagToInline.getNode();
|
||||
|
||||
if (node == null) {
|
||||
throw new AndroidRefactoringErrorException();
|
||||
}
|
||||
final ASTNode startTagEnd = XmlChildRole.START_TAG_END_FINDER.findChild(node);
|
||||
final ASTNode closingTagStart = XmlChildRole.CLOSING_TAG_START_FINDER.findChild(node);
|
||||
|
||||
if (startTagEnd == null || closingTagStart == null) {
|
||||
throw new AndroidRefactoringErrorException();
|
||||
}
|
||||
final int contentStart = startTagEnd.getTextRange().getEndOffset();
|
||||
final int contentEnd = closingTagStart.getTextRange().getStartOffset();
|
||||
|
||||
if (contentStart < 0 || contentEnd < 0 || contentStart >= contentEnd) {
|
||||
throw new AndroidRefactoringErrorException();
|
||||
}
|
||||
final PsiFile file = tagToInline.getContainingFile();
|
||||
|
||||
if(file == null) {
|
||||
throw new AndroidRefactoringErrorException();
|
||||
}
|
||||
final String textToInline = file.getText().
|
||||
substring(contentStart, contentEnd).trim();
|
||||
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
|
||||
final Document document = documentManager.getDocument(tagToReplace.getContainingFile());
|
||||
|
||||
if (document == null) {
|
||||
throw new AndroidRefactoringErrorException();
|
||||
}
|
||||
final TextRange range = tagToReplace.getTextRange();
|
||||
document.replaceString(range.getStartOffset(), range.getEndOffset(), textToInline);
|
||||
documentManager.commitDocument(document);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</merge>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</merge>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</merge>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://wrongNamespace"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://wrongNamespace"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</merge>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:andr="http://schemas.android.com/apk/res/android"
|
||||
andr:orientation="vertical"
|
||||
andr:layout_width="fill_parent"
|
||||
andr:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:andr="http://schemas.android.com/apk/res/android"
|
||||
andr:orientation="vertical"
|
||||
andr:layout_width="fill_parent"
|
||||
andr:layout_height="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="1"/>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="2"/>
|
||||
</merge>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/included"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,5 @@
|
||||
<caret><?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"/>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/included"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,5 @@
|
||||
<caret><?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"/>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<i<caret>nclude layout="@layout/included"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<i<caret>nclude layout="@layout/included"/>
|
||||
<include layout="@layout/included"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/included"/>
|
||||
<Te<caret>xtView/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<inc<caret>lude layout="@layout/unknown"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView xmlns:andr="http://schemas.android.com/apk/res/android"
|
||||
andr:layout_width="fill_parent"
|
||||
andr:layout_height="wrap_content"
|
||||
andr:text="Hello World, MyActivity"
|
||||
andr:background="@android:color/holo_red_dark"/>
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:andr="http://schemas.android.com/apk/res/android"
|
||||
andr:layout_width="fill_parent"
|
||||
andr:layout_height="fill_parent"
|
||||
andr:text="Hello World, MyActivity"
|
||||
andr:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.example"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
app:customAttr="true"/>
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.example"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
app:customAttr="true"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
xmlns="http://myNamespace"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns="http://myNamespace"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.example"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.example"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
app:customAttr="true"/>
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.example"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
app:customAttr="true"/>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns="http://myNamespace"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns="http://myNamespace"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns="http://myNamespace"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/includ<caret>ed" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||
<include layout="@layout/included" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,6 @@
|
||||
<caret><?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World, MyActivity"
|
||||
android:background="@android:color/holo_red_dark"/>
|
||||
@@ -0,0 +1,12 @@
|
||||
package p1.p2;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
|
||||
public class MyActivity extends Activity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
int n = R.layout.included;
|
||||
}
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
package org.jetbrains.android.refactoring;
|
||||
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.refactoring.actions.InlineAction;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.android.AndroidTestCase;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidInlineLayoutTest extends AndroidTestCase {
|
||||
private static final String BASE_PATH = "refactoring/inlineLayout/";
|
||||
|
||||
public void test1() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test2() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test3() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test4() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test5() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test6() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test7() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test8() throws Exception {
|
||||
doTestCommonInlineAll();
|
||||
}
|
||||
|
||||
public void test9() throws Exception {
|
||||
final String testName = getTestName(true);
|
||||
myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/layout/test.xml");
|
||||
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + "_included.xml", "res/layout/included.xml");
|
||||
myFixture.configureFromExistingVirtualFile(f);
|
||||
AndroidInlineLayoutHandler.setTestConfig(new AndroidInlineTestConfig(true));
|
||||
try {
|
||||
final Presentation p = myFixture.testAction(new InlineAction());
|
||||
assertTrue(p.isEnabled());
|
||||
assertTrue(p.isVisible());
|
||||
}
|
||||
finally {
|
||||
AndroidInlineLayoutHandler.setTestConfig(null);
|
||||
}
|
||||
myFixture.checkResultByFile("res/layout/test.xml", BASE_PATH + testName + "_after.xml", true);
|
||||
assertNull(myFixture.getTempDirFixture().getFile("res/layout/included.xml"));
|
||||
}
|
||||
|
||||
public void test10() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test11() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test12() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test13() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test14() throws Exception {
|
||||
doTestCommonInlineThisOnly();
|
||||
}
|
||||
|
||||
public void test15() throws Exception {
|
||||
myFixture.copyFileToProject(BASE_PATH + getTestName(true) + "_included.xml", "res/layout-land/included.xml");
|
||||
doTestCommonInlineActionError(false, true);
|
||||
}
|
||||
|
||||
public void test16() throws Exception {
|
||||
myFixture.copyFileToProject("R.java", "gen/p1/p2/R.java");
|
||||
myFixture.copyFileToProject(BASE_PATH + "MyActivity.java", "src/p1/p2/MyActivity.java");
|
||||
doTestCommonInlineActionError(false, true);
|
||||
}
|
||||
|
||||
public void test17() throws Exception {
|
||||
doTestInlineIncludeAction(true);
|
||||
}
|
||||
|
||||
public void test18() throws Exception {
|
||||
doTestInlineIncludeAction(false);
|
||||
}
|
||||
|
||||
public void test19() throws Exception {
|
||||
doTestInlineIncludeActionDisabled();
|
||||
}
|
||||
|
||||
public void test20() throws Exception {
|
||||
doTestInlineIncludeActionError(true);
|
||||
}
|
||||
|
||||
private void doTestCommonInlineThisOnly() throws Exception {
|
||||
doTestCommonInlineAction(true);
|
||||
myFixture.checkResultByFile("res/layout/included.xml",
|
||||
BASE_PATH + getTestName(true) + "_included.xml", true);
|
||||
}
|
||||
|
||||
private void doTestCommonInlineAll() throws Exception {
|
||||
doTestCommonInlineAction(false);
|
||||
assertNull(myFixture.getTempDirFixture().getFile("res/layout/included.xml"));
|
||||
}
|
||||
|
||||
private void doTestCommonInlineActionError(boolean inlineThisOnly, boolean configureFromIncludedFile) {
|
||||
final String testName = getTestName(true);
|
||||
final VirtualFile included = myFixture.copyFileToProject(BASE_PATH + testName + "_included.xml", "res/layout/included.xml");
|
||||
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/layout/test.xml");
|
||||
myFixture.configureFromExistingVirtualFile(configureFromIncludedFile ? included : f);
|
||||
AndroidInlineLayoutHandler.setTestConfig(new AndroidInlineTestConfig(inlineThisOnly));
|
||||
try {
|
||||
myFixture.testAction(new InlineAction());
|
||||
fail();
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
assertTrue(e.getMessage().length() > 0);
|
||||
}
|
||||
finally {
|
||||
AndroidInlineLayoutHandler.setTestConfig(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void doTestCommonInlineAction(boolean inlineThisOnly) {
|
||||
final String testName = getTestName(true);
|
||||
myFixture.copyFileToProject(BASE_PATH + testName + "_included.xml", "res/layout/included.xml");
|
||||
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/layout/test.xml");
|
||||
myFixture.configureFromExistingVirtualFile(f);
|
||||
AndroidInlineLayoutHandler.setTestConfig(new AndroidInlineTestConfig(inlineThisOnly));
|
||||
try {
|
||||
final Presentation p = myFixture.testAction(new InlineAction());
|
||||
assertTrue(p.isEnabled());
|
||||
assertTrue(p.isVisible());
|
||||
}
|
||||
finally {
|
||||
AndroidInlineLayoutHandler.setTestConfig(null);
|
||||
}
|
||||
myFixture.checkResultByFile(BASE_PATH + testName + "_after.xml", true);
|
||||
}
|
||||
|
||||
private void doTestInlineIncludeAction(boolean inlineThisOnly) {
|
||||
final String testName = getTestName(true);
|
||||
myFixture.copyFileToProject(BASE_PATH + testName + "_included.xml", "res/layout/included.xml");
|
||||
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/layout/test.xml");
|
||||
myFixture.configureFromExistingVirtualFile(f);
|
||||
final Presentation p = myFixture.testAction(new AndroidInlineIncludeAction(new AndroidInlineTestConfig(inlineThisOnly)));
|
||||
assertTrue(p.isEnabled());
|
||||
assertTrue(p.isVisible());
|
||||
myFixture.checkResultByFile(BASE_PATH + testName + "_after.xml", true);
|
||||
}
|
||||
|
||||
private void doTestInlineIncludeActionDisabled() {
|
||||
final String testName = getTestName(true);
|
||||
myFixture.copyFileToProject(BASE_PATH + testName + "_included.xml", "res/layout/included.xml");
|
||||
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/layout/test.xml");
|
||||
myFixture.configureFromExistingVirtualFile(f);
|
||||
final Presentation p = myFixture.testAction(new AndroidInlineIncludeAction(new AndroidInlineTestConfig(true)));
|
||||
assertTrue(p.isVisible());
|
||||
assertFalse(p.isEnabled());
|
||||
}
|
||||
|
||||
private void doTestInlineIncludeActionError(boolean inlineThisOnly) {
|
||||
final String testName = getTestName(true);
|
||||
myFixture.copyFileToProject(BASE_PATH + testName + "_included.xml", "res/layout/included.xml");
|
||||
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + testName + ".xml", "res/layout/test.xml");
|
||||
myFixture.configureFromExistingVirtualFile(f);
|
||||
try {
|
||||
myFixture.testAction(new AndroidInlineIncludeAction(new AndroidInlineTestConfig(inlineThisOnly)));
|
||||
fail();
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
assertTrue(e.getMessage().length() > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user