aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java246
1 files changed, 246 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java
new file mode 100644
index 000000000..4eff2cde5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.editors.layout.refactoring;
+
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.SdkConstants.EXT_XML;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.common.xml.XmlFormatStyle;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Removes the layout surrounding the current selection (or if the current selection has
+ * children, removes the current layout), and migrates namespace and layout attributes.
+ */
+@SuppressWarnings("restriction") // XML model
+public class UnwrapRefactoring extends VisualRefactoring {
+ private Element mContainer;
+
+ /**
+ * This constructor is solely used by {@link Descriptor},
+ * to replay a previous refactoring.
+ * @param arguments argument map created by #createArgumentMap.
+ */
+ UnwrapRefactoring(Map<String, String> arguments) {
+ super(arguments);
+ }
+
+ public UnwrapRefactoring(
+ IFile file,
+ LayoutEditorDelegate delegate,
+ ITextSelection selection,
+ ITreeSelection treeSelection) {
+ super(file, delegate, selection, treeSelection);
+ }
+
+ @VisibleForTesting
+ UnwrapRefactoring(List<Element> selectedElements, LayoutEditorDelegate editor) {
+ super(selectedElements, editor);
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ pm.beginTask("Checking preconditions...", 6);
+
+ if (mSelectionStart == -1 || mSelectionEnd == -1) {
+ status.addFatalError("No selection to wrap");
+ return status;
+ }
+
+ // Make sure that the selection all has the same parent?
+ if (mElements.size() == 0) {
+ status.addFatalError("Nothing to unwrap");
+ return status;
+ }
+
+ Element first = mElements.get(0);
+
+ // Determine the element of the container to be removed.
+ // If you've selected a non-container, or you've selected multiple
+ // elements, then it's the parent which should be removed. Otherwise,
+ // it's the selection itself which represents the container.
+ boolean useParent = mElements.size() > 1;
+ if (!useParent) {
+ if (DomUtilities.getChildren(first).size() == 0) {
+ useParent = true;
+ }
+ }
+ Node parent = first.getParentNode();
+ if (parent instanceof Document) {
+ mContainer = first;
+ List<Element> elements = DomUtilities.getChildren(mContainer);
+ if (elements.size() == 0) {
+ status.addFatalError(
+ "Cannot remove container when it has no children");
+ return status;
+ }
+ } else if (useParent && (parent instanceof Element)) {
+ mContainer = (Element) parent;
+ } else {
+ mContainer = first;
+ }
+
+ for (Element element : mElements) {
+ if (element.getParentNode() != parent) {
+ status.addFatalError(
+ "All unwrapped elements must share the same parent element");
+ return status;
+ }
+ }
+
+ // Ensure that if we are removing the root, that it has only one child
+ // such that there is a new single root
+ if (mContainer.getParentNode() instanceof Document) {
+ if (DomUtilities.getChildren(mContainer).size() > 1) {
+ status.addFatalError(
+ "Cannot remove root: it has more than one child "
+ + "which would result in multiple new roots");
+ return status;
+ }
+ }
+
+ pm.worked(1);
+ return status;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ @Override
+ protected VisualRefactoringDescriptor createDescriptor() {
+ String comment = getName();
+ return new Descriptor(
+ mProject.getName(), //project
+ comment, //description
+ comment, //comment
+ createArgumentMap());
+ }
+
+ @Override
+ public String getName() {
+ return "Remove Container";
+ }
+
+ @Override
+ protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) {
+ // (1) If the removed parent is the root container, transfer its
+ // namespace declarations
+ // (2) Remove the root element completely
+ // (3) Transfer layout attributes?
+ // (4) Check for Java R.file usages?
+
+ IFile file = mDelegate.getEditor().getInputFile();
+ List<Change> changes = new ArrayList<Change>();
+ if (file == null) {
+ return changes;
+ }
+ MultiTextEdit rootEdit = new MultiTextEdit();
+
+ // Transfer namespace elements?
+ if (mContainer.getParentNode() instanceof Document) {
+ List<Element> elements = DomUtilities.getChildren(mContainer);
+ assert elements.size() == 1;
+ Element newRoot = elements.get(0);
+
+ List<Attr> declarations = findNamespaceAttributes(mContainer);
+ for (Attr attribute : declarations) {
+ if (attribute instanceof IndexedRegion) {
+ setAttribute(rootEdit, newRoot, attribute.getNamespaceURI(),
+ attribute.getPrefix(), attribute.getLocalName(), attribute.getValue());
+ }
+ }
+ }
+
+ // Transfer layout_ attributes (other than width and height)
+ List<Element> children = DomUtilities.getChildren(mContainer);
+ if (children.size() == 1) {
+ List<Attr> layoutAttributes = findLayoutAttributes(mContainer);
+ for (Attr attribute : layoutAttributes) {
+ String name = attribute.getLocalName();
+ if ((name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT))
+ && ANDROID_URI.equals(attribute.getNamespaceURI())) {
+ // Already handled specially
+ continue;
+ }
+ }
+ }
+
+ // Remove the root
+ removeElementTags(rootEdit, mContainer, Collections.<Element>emptyList() /* skip */,
+ false /*changeIndentation*/);
+
+ MultiTextEdit formatted = reformat(rootEdit, XmlFormatStyle.LAYOUT);
+ if (formatted != null) {
+ rootEdit = formatted;
+ }
+
+ TextFileChange change = new TextFileChange(file.getName(), file);
+ change.setEdit(rootEdit);
+ change.setTextType(EXT_XML);
+ changes.add(change);
+ return changes;
+ }
+
+ @Override
+ public VisualRefactoringWizard createWizard() {
+ return new UnwrapWizard(this, mDelegate);
+ }
+
+ public static class Descriptor extends VisualRefactoringDescriptor {
+ public Descriptor(String project, String description, String comment,
+ Map<String, String> arguments) {
+ super("com.android.ide.eclipse.adt.refactoring.unwrap", //$NON-NLS-1$
+ project, description, comment, arguments);
+ }
+
+ @Override
+ protected Refactoring createRefactoring(Map<String, String> args) {
+ return new UnwrapRefactoring(args);
+ }
+ }
+}