ChangeLayoutRefactoring.java revision f5256a38ef2e429b5efd4482808bc902bf7634e3
18dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye/*
28dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * Copyright (C) 2011 The Android Open Source Project
38dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye *
48dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * Licensed under the Eclipse Public License, Version 1.0 (the "License");
58dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * you may not use this file except in compliance with the License.
68dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * You may obtain a copy of the License at
78dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye *
88dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye *      http://www.eclipse.org/org/documents/epl-v10.php
98dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye *
108dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * Unless required by applicable law or agreed to in writing, software
118dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * distributed under the License is distributed on an "AS IS" BASIS,
128dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * See the License for the specific language governing permissions and
148dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * limitations under the License.
158dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye */
168dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyepackage com.android.ide.eclipse.adt.internal.editors.layout.refactoring;
178dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
188dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
198dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.ATTR_BASELINE_ALIGNED;
218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ALIGN_BASELINE;
228dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_BELOW;
238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_TO_RIGHT_OF;
248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION;
250757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport static com.android.ide.common.layout.LayoutConstants.FQCN_GESTURE_OVERLAY_VIEW;
268dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.FQCN_LINEAR_LAYOUT;
278dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.FQCN_RELATIVE_LAYOUT;
288dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.FQCN_TABLE_LAYOUT;
290757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport static com.android.ide.common.layout.LayoutConstants.GESTURE_OVERLAY_VIEW;
308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.LINEAR_LAYOUT;
318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.TABLE_ROW;
328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.VALUE_FALSE;
338dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
34c3105b949cd2a0f6cbf8a12ec4f30e49b5b5a502Xavier Ducrohetimport static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
358dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
360757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport com.android.annotations.VisibleForTesting;
378dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
38f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatStyle;
398dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
408dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
410757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
420757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas;
430757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.ViewHierarchy;
44f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbyeimport com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
458dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
468dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.core.resources.IFile;
478dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.core.runtime.CoreException;
488dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.core.runtime.IProgressMonitor;
498dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.core.runtime.OperationCanceledException;
508dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.jface.text.ITextSelection;
518dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.jface.viewers.ITreeSelection;
528dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.ltk.core.refactoring.Change;
538dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.ltk.core.refactoring.Refactoring;
548dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.ltk.core.refactoring.RefactoringStatus;
558dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.ltk.core.refactoring.TextFileChange;
568dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.text.edits.MultiTextEdit;
578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.text.edits.ReplaceEdit;
589e6db060854d0e890190919a27a1846f50f69d1aTor Norbyeimport org.eclipse.text.edits.TextEdit;
599e6db060854d0e890190919a27a1846f50f69d1aTor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
608dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
619e6db060854d0e890190919a27a1846f50f69d1aTor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
628dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.w3c.dom.Attr;
638dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.w3c.dom.Element;
648dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.w3c.dom.Node;
658dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.w3c.dom.NodeList;
668dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
678dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.ArrayList;
688dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.HashSet;
698dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.List;
708dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.Map;
718dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.Set;
728dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
738dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye/**
748dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * Converts the selected layout into a layout of a different type.
758dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye */
768dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye@SuppressWarnings("restriction") // XML model
778dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyepublic class ChangeLayoutRefactoring extends VisualRefactoring {
780757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    private static final String KEY_TYPE = "type";       //$NON-NLS-1$
790757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    private static final String KEY_FLATTEN = "flatten"; //$NON-NLS-1$
808dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
818dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private String mTypeFqcn;
820757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    private boolean mFlatten;
838dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
848dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /**
858dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * This constructor is solely used by {@link Descriptor},
868dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * to replay a previous refactoring.
878dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param arguments argument map created by #createArgumentMap.
888dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     */
898dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    ChangeLayoutRefactoring(Map<String, String> arguments) {
908dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        super(arguments);
918dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        mTypeFqcn = arguments.get(KEY_TYPE);
920757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        mFlatten = Boolean.parseBoolean(arguments.get(KEY_FLATTEN));
930757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    }
940757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
950757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    @VisibleForTesting
960757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    ChangeLayoutRefactoring(List<Element> selectedElements, LayoutEditor editor) {
970757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        super(selectedElements, editor);
988dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
998dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1008dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    public ChangeLayoutRefactoring(IFile file, LayoutEditor editor, ITextSelection selection,
1018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            ITreeSelection treeSelection) {
1028dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        super(file, editor, selection, treeSelection);
1038dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1048dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1058dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
1068dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
1078dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            OperationCanceledException {
1088dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        RefactoringStatus status = new RefactoringStatus();
1098dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1108dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        try {
1118dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            pm.beginTask("Checking preconditions...", 2);
1128dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1138dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (mSelectionStart == -1 || mSelectionEnd == -1) {
1148dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                status.addFatalError("No selection to convert");
1158dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                return status;
1168dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
1178dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1188dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (mElements.size() != 1) {
1198dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                status.addFatalError("Select precisely one layout to convert");
1208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                return status;
1218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
1228dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            pm.worked(1);
1248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            return status;
1258dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1268dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } finally {
1278dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            pm.done();
1288dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
1298dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
1328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    protected VisualRefactoringDescriptor createDescriptor() {
1338dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String comment = getName();
1348dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return new Descriptor(
1358dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                mProject.getName(), //project
1368dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                comment, //description
1378dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                comment, //comment
1388dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                createArgumentMap());
1398dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1408dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1418dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
1428dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    protected Map<String, String> createArgumentMap() {
1438dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Map<String, String> args = super.createArgumentMap();
1448dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        args.put(KEY_TYPE, mTypeFqcn);
1450757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        args.put(KEY_FLATTEN, Boolean.toString(mFlatten));
1468dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1478dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return args;
1488dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1498dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1508dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
1518dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    public String getName() {
1528dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return "Change Layout";
1538dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1548dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1558dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    void setType(String typeFqcn) {
1568dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        mTypeFqcn = typeFqcn;
1578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1588dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1590757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    void setFlatten(boolean flatten) {
1600757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        mFlatten = flatten;
1610757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    }
1620757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
1630757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    @Override
1640757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    protected List<Element> initElements() {
1650757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        List<Element> elements = super.initElements();
1660757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
1670757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        // Don't convert a root GestureOverlayView; convert its child. This looks for
1680757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        // gesture overlays, and if found, it generates a new child list where the gesture
1690757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        // overlay children are replaced by their first element children
1700757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        for (Element element : elements) {
1710757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            String tagName = element.getTagName();
1720757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            if (tagName.equals(GESTURE_OVERLAY_VIEW)
1730757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    || tagName.equals(FQCN_GESTURE_OVERLAY_VIEW)) {
1740757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                List<Element> replacement = new ArrayList<Element>(elements.size());
1750757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                for (Element e : elements) {
1760757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    tagName = e.getTagName();
1770757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    if (tagName.equals(GESTURE_OVERLAY_VIEW)
1780757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            || tagName.equals(FQCN_GESTURE_OVERLAY_VIEW)) {
1790757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        NodeList children = e.getChildNodes();
1800757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        Element first = null;
1810757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        for (int i = 0, n = children.getLength(); i < n; i++) {
1820757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            Node node = children.item(i);
1830757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            if (node.getNodeType() == Node.ELEMENT_NODE) {
1840757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                                first = (Element) node;
1850757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                                break;
1860757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            }
1870757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        }
1880757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        if (first != null) {
1890757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            e = first;
1900757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        }
1910757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    }
1920757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    replacement.add(e);
1930757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                }
1940757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                return replacement;
1950757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            }
1960757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        }
1970757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
1980757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        return elements;
1990757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    }
2000757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
2018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
202eacf5a536e29b366e6e2dcf6a80d86ad5241734cTor Norbye    protected List<Change> computeChanges(IProgressMonitor monitor) {
2038dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String name = getViewClass(mTypeFqcn);
2048dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2058dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        IFile file = mEditor.getInputFile();
2068dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        List<Change> changes = new ArrayList<Change>();
2078dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        TextFileChange change = new TextFileChange(file.getName(), file);
2088dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        MultiTextEdit rootEdit = new MultiTextEdit();
2098dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        change.setTextType(EXT_XML);
2108dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        changes.add(change);
2118dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2128dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String text = getText(mSelectionStart, mSelectionEnd);
2138dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Element layout = getPrimaryElement();
2148dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String oldName = layout.getNodeName();
2158dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        int open = text.indexOf(oldName);
2168dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        int close = text.lastIndexOf(oldName);
2178dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2188dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        if (open != -1 && close != -1) {
2198dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            int oldLength = oldName.length();
2208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            rootEdit.addChild(new ReplaceEdit(mSelectionStart + open, oldLength, name));
2218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (close != open) { // Gracefully handle <FooLayout/>
2228dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                rootEdit.addChild(new ReplaceEdit(mSelectionStart + close, oldLength, name));
2238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
2248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
2258dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2269e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        String oldId = getId(layout);
2279e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        String newId = ensureIdMatchesType(layout, mTypeFqcn, rootEdit);
2289e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        // Update any layout references to the old id with the new id
2299e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        if (oldId != null && newId != null) {
2309e6db060854d0e890190919a27a1846f50f69d1aTor Norbye            IStructuredModel model = mEditor.getModelForRead();
2319e6db060854d0e890190919a27a1846f50f69d1aTor Norbye            try {
2329e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                IStructuredDocument doc = model.getStructuredDocument();
2339e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                if (doc != null) {
2349e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                    List<TextEdit> replaceIds = replaceIds(getAndroidNamespacePrefix(), doc,
2359e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                            mSelectionStart,
2369e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                            mSelectionEnd, oldId, newId);
2379e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                    for (TextEdit edit : replaceIds) {
2389e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                        rootEdit.addChild(edit);
2399e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                    }
2409e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                }
2419e6db060854d0e890190919a27a1846f50f69d1aTor Norbye            } finally {
2429e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                model.releaseFromRead();
2439e6db060854d0e890190919a27a1846f50f69d1aTor Norbye            }
2449e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        }
2450757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
2468dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String oldType = getOldType();
2478dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String newType = mTypeFqcn;
2480757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        if (newType.equals(FQCN_RELATIVE_LAYOUT)) {
2490757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            if (oldType.equals(FQCN_LINEAR_LAYOUT) && !mFlatten) {
2500757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // Hand-coded conversion specifically tailored for linear to relative, provided
2510757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // there is no hierarchy flattening
2520757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // TODO: use the RelativeLayoutConversionHelper for this; it does a better job
2530757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // analyzing gravities etc.
2540757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                convertLinearToRelative(rootEdit);
2550757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                removeUndefinedLayoutAttrs(rootEdit, layout);
2560757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            } else {
2570757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // Generic conversion to relative - can also flatten the hierarchy
2580757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                convertAnyToRelative(rootEdit);
2590757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // This already handles removing undefined layout attributes -- right?
2600757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                //removeUndefinedLayoutAttrs(rootEdit, layout);
2610757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            }
2628dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } else if (oldType.equals(FQCN_RELATIVE_LAYOUT) && newType.equals(FQCN_LINEAR_LAYOUT)) {
2638dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            convertRelativeToLinear(rootEdit);
2640757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            removeUndefinedLayoutAttrs(rootEdit, layout);
2658dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } else if (oldType.equals(FQCN_LINEAR_LAYOUT) && newType.equals(FQCN_TABLE_LAYOUT)) {
2668dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            convertLinearToTable(rootEdit);
2670757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            removeUndefinedLayoutAttrs(rootEdit, layout);
2688dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } else {
2698dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            convertGeneric(rootEdit, oldType, newType);
2700757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            removeUndefinedLayoutAttrs(rootEdit, layout);
2718dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
2728dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
273f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye        if (AdtPrefs.getPrefs().getFormatGuiXml()) {
274f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye            MultiTextEdit formatted = reformat(rootEdit, XmlFormatStyle.LAYOUT);
275f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye            if (formatted != null) {
276f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye                rootEdit = formatted;
277f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye            }
278f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye        }
279f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye        change.setEdit(rootEdit);
280f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye
2818dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return changes;
2828dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
2838dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2848dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /** Hand coded conversion from a LinearLayout to a TableLayout */
2858dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void convertLinearToTable(MultiTextEdit rootEdit) {
2868dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // This is pretty easy; just switch the root tag (already done by the initial generic
2878dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // conversion) and then convert all the children into <TableRow> elements.
2888dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // Finally, get rid of the orientation attribute, if any.
2898dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Element layout = getPrimaryElement();
2908dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        removeOrientationAttribute(rootEdit, layout);
2918dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2928dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        NodeList children = layout.getChildNodes();
2938dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        for (int i = 0, n = children.getLength(); i < n; i++) {
2948dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            Node node = children.item(i);
2958dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (node.getNodeType() == Node.ELEMENT_NODE) {
2968dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Element child = (Element) node;
2978dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                if (node instanceof IndexedRegion) {
2988dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    IndexedRegion region = (IndexedRegion) node;
2998dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    int start = region.getStartOffset();
3008dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    int end = region.getEndOffset();
3018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    String text = getText(start, end);
3028dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    String oldName = child.getNodeName();
3038dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    if (oldName.equals(LINEAR_LAYOUT)) {
3048dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        removeOrientationAttribute(rootEdit, child);
3058dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        int open = text.indexOf(oldName);
3068dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        int close = text.lastIndexOf(oldName);
3078dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3088dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        if (open != -1 && close != -1) {
3098dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                            int oldLength = oldName.length();
3108dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                            rootEdit.addChild(new ReplaceEdit(mSelectionStart + open, oldLength,
3118dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                    TABLE_ROW));
3128dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                            if (close != open) { // Gracefully handle <FooLayout/>
3138dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                rootEdit.addChild(new ReplaceEdit(mSelectionStart + close,
3148dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                        oldLength, TABLE_ROW));
3158dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                            }
3168dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        }
3178dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    } // else: WRAP in TableLayout!
3188dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                }
3198dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
3208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
3218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
3228dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     /** Hand coded conversion from a LinearLayout to a RelativeLayout */
3248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void convertLinearToRelative(MultiTextEdit rootEdit) {
3258dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // This can be done accurately.
3268dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Element layout = getPrimaryElement();
3278dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // Horizontal is the default, so if no value is specified it is horizontal.
3288dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        boolean isVertical = VALUE_VERTICAL.equals(layout.getAttributeNS(ANDROID_URI,
3298dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                ATTR_ORIENTATION));
3308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        removeOrientationAttribute(rootEdit, layout);
3328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3338dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String attributePrefix = getAndroidNamespacePrefix();
3348dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3358dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Consider gravity of each element
3368dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Consider weight of each element
3378dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // Right now it simply makes a single attachment to keep the order.
3388dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3398dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        if (isVertical) {
3408dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            // Align each child to the bottom and left of its parent
3418dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            NodeList children = layout.getChildNodes();
3428dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            String prevId = null;
3438dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            for (int i = 0, n = children.getLength(); i < n; i++) {
3448dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Node node = children.item(i);
3458dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                if (node.getNodeType() == Node.ELEMENT_NODE) {
3468dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    Element child = (Element) node;
3470757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    String id = ensureHasId(rootEdit, child, null);
3488dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    if (prevId != null) {
3490757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
3508dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                ATTR_LAYOUT_BELOW, prevId);
3518dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    }
3528dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    prevId = id;
3538dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                }
3548dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
3558dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } else {
3568dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            // Align each child to the left
3578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            NodeList children = layout.getChildNodes();
3588dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            boolean isBaselineAligned =
3598dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                !VALUE_FALSE.equals(layout.getAttributeNS(ANDROID_URI, ATTR_BASELINE_ALIGNED));
3608dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3618dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            String prevId = null;
3628dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            for (int i = 0, n = children.getLength(); i < n; i++) {
3638dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Node node = children.item(i);
3648dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                if (node.getNodeType() == Node.ELEMENT_NODE) {
3658dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    Element child = (Element) node;
3660757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    String id = ensureHasId(rootEdit, child, null);
3678dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    if (prevId != null) {
3680757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
3698dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                ATTR_LAYOUT_TO_RIGHT_OF, prevId);
3708dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        if (isBaselineAligned) {
3710757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
3728dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                    ATTR_LAYOUT_ALIGN_BASELINE, prevId);
3738dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        }
3748dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    }
3758dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    prevId = id;
3768dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                }
3778dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
3788dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
3798dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
3808dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3818dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /** Strips out the android:orientation attribute from the given linear layout element */
3828dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void removeOrientationAttribute(MultiTextEdit rootEdit, Element layout) {
3838dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        assert layout.getTagName().equals(LINEAR_LAYOUT);
3848dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        removeAttribute(rootEdit, layout, ANDROID_URI, ATTR_ORIENTATION);
3858dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
3868dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3878dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /**
3888dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * Hand coded conversion from a RelativeLayout to a LinearLayout
3898dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     *
3908dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param rootEdit the root multi text edit to add edits to
3918dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     */
3928dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void convertRelativeToLinear(MultiTextEdit rootEdit) {
3938dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // This is going to be lossy...
3948dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Attempt to "order" the items based on their visual positions
3958dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // and insert them in that order in the LinearLayout.
3968dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Possibly use nesting if necessary, by spatial subdivision,
3978dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // to accomplish roughly the same layout as the relative layout specifies.
3988dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
3998dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4008dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /**
4018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * Hand coded -generic- conversion from one layout to another. This is not going to be
4028dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * an accurate layout transformation; instead it simply migrates the layout attributes
4038dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * that are supported, and adds defaults for any new required layout attributes. In
4048dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * addition, it attempts to order the children visually based on where they fit in a
4058dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * rendering. (Unsupported layout attributes will be removed by the caller at the
4068dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * end.)
4078dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * <ul>
4088dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * <li>Try to handle nesting. Converting a *hierarchy* of layouts into a flatter
4098dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * layout for powerful layouts that support it, like RelativeLayout.
4108dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * <li>Try to do automatic "inference" about the layout. I can render it and look at
4118dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * the ViewInfo positions and sizes. I can render it multiple times, at different
4128dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * sizes, to infer "stretchiness" and "weight" properties of the children.
4138dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * <li>Try to do indirect transformations. E.g. if I can go from A to B, and B to C,
4148dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * then an attempt to go from A to C should perform conversions A to B and then B to
4158dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * C.
4168dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * </ul>
4178dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     *
4188dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param rootEdit the root multi text edit to add edits to
4198dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param oldType the fully qualified class name of the layout type we are converting
4208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     *            from
4218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param newType the fully qualified class name of the layout type we are converting
4228dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     *            to
4238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     */
4248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void convertGeneric(MultiTextEdit rootEdit, String oldType, String newType) {
4258dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Add hooks for 3rd party conversions getting registered through the
4268dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // IViewRule interface.
4278dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4288dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // For now we simply go with the default behavior, which is to just strip the
4298dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // layout attributes that aren't supported.
4308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
4318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /** Removes all the unused attributes after a conversion */
4338dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void removeUndefinedLayoutAttrs(MultiTextEdit rootEdit, Element layout) {
4340757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        ViewElementDescriptor descriptor = getElementDescriptor(mTypeFqcn);
4358dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        if (descriptor == null) {
4368dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            return;
4378dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
4388dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4398dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Set<String> defined = new HashSet<String>();
4408dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        AttributeDescriptor[] layoutAttributes = descriptor.getLayoutAttributes();
4418dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        for (AttributeDescriptor attribute : layoutAttributes) {
4428dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            defined.add(attribute.getXmlLocalName());
4438dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
4448dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4458dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        NodeList children = layout.getChildNodes();
4468dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        for (int i = 0, n = children.getLength(); i < n; i++) {
4478dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            Node node = children.item(i);
4488dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (node.getNodeType() == Node.ELEMENT_NODE) {
4498dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Element child = (Element) node;
4508dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4518dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                List<Attr> attributes = findLayoutAttributes(child);
4528dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                for (Attr attribute : attributes) {
4538dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    String name = attribute.getLocalName();
4548dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    if (!defined.contains(name)) {
4558dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        // Remove it
4568dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        removeAttribute(rootEdit, child, attribute.getNamespaceURI(), name);
4578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    }
4588dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                }
4598dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
4608dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
4618dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
4628dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4630757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    /** Hand coded conversion from any layout to a RelativeLayout */
4640757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    private void convertAnyToRelative(MultiTextEdit rootEdit) {
4650757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        // To perform a conversion from any other layout type, including nested conversion,
4660757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        Element layout = getPrimaryElement();
4670757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        CanvasViewInfo rootView = mRootView;
4680757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        if (rootView == null) {
4690757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            LayoutCanvas canvas = mEditor.getGraphicalEditor().getCanvasControl();
4700757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            ViewHierarchy viewHierarchy = canvas.getViewHierarchy();
4710757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            rootView = viewHierarchy.getRoot();
4728dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
4738dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4740757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        RelativeLayoutConversionHelper helper =
4750757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            new RelativeLayoutConversionHelper(this, layout, mFlatten, rootEdit, rootView);
4760757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        helper.convertToRelative();
4778dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
4788dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4798dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    public static class Descriptor extends VisualRefactoringDescriptor {
4808dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        public Descriptor(String project, String description, String comment,
4818dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Map<String, String> arguments) {
4828dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            super("com.android.ide.eclipse.adt.refactoring.convert", //$NON-NLS-1$
4838dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    project, description, comment, arguments);
4848dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
4858dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4868dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        @Override
4878dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        protected Refactoring createRefactoring(Map<String, String> args) {
4888dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            return new ChangeLayoutRefactoring(args);
4898dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
4908dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
4918dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4928dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    String getOldType() {
4938dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Element primary = getPrimaryElement();
4948dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        if (primary != null) {
4958dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            String oldType = primary.getTagName();
4968dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (oldType.indexOf('.') == -1) {
4978dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                oldType = ANDROID_WIDGET_PREFIX + oldType;
4988dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
4998dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            return oldType;
5008dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
5018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
5028dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return null;
5038dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
5040757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
5050757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    @VisibleForTesting
5060757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    protected CanvasViewInfo mRootView;
5070757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
5080757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    @VisibleForTesting
5090757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    public void setRootView(CanvasViewInfo rootView) {
5100757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        mRootView = rootView;
5110757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    }
512891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye
513891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye    @Override
514891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye    VisualRefactoringWizard createWizard() {
515891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye        return new ChangeLayoutWizard(this, mEditor);
516891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye    }
5178dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye}
518