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
1812d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ANDROID_URI;
1912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ANDROID_WIDGET_PREFIX;
2012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_BASELINE_ALIGNED;
2112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE;
2212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_LAYOUT_BELOW;
2312d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
2412d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
2512d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF;
2612d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
2712d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_ORIENTATION;
2812d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.EXT_XML;
2912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FQCN_GESTURE_OVERLAY_VIEW;
3012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FQCN_GRID_LAYOUT;
3112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FQCN_LINEAR_LAYOUT;
3212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FQCN_RELATIVE_LAYOUT;
3312d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FQCN_TABLE_LAYOUT;
3412d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.GESTURE_OVERLAY_VIEW;
3512d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.LINEAR_LAYOUT;
3612d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.TABLE_ROW;
3712d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.VALUE_FALSE;
3812d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.VALUE_VERTICAL;
3912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.VALUE_WRAP_CONTENT;
4012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye
4112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport com.android.SdkConstants;
427e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbyeimport com.android.annotations.NonNull;
430757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport com.android.annotations.VisibleForTesting;
444cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbyeimport com.android.ide.eclipse.adt.AdtPlugin;
458dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
46f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatStyle;
47e50549f54810c29bffc681e39d33158ab2a8f26eRaphaelimport com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
488dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
490757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
5078d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
510757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas;
520757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.ViewHierarchy;
53f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbyeimport com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
548dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
558dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.core.resources.IFile;
568dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.core.runtime.CoreException;
578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.core.runtime.IProgressMonitor;
584cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbyeimport org.eclipse.core.runtime.IStatus;
598dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.core.runtime.OperationCanceledException;
608dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.jface.text.ITextSelection;
618dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.jface.viewers.ITreeSelection;
628dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.ltk.core.refactoring.Change;
638dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.ltk.core.refactoring.Refactoring;
648dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.ltk.core.refactoring.RefactoringStatus;
658dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.ltk.core.refactoring.TextFileChange;
664cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbyeimport org.eclipse.text.edits.MalformedTreeException;
678dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.text.edits.MultiTextEdit;
688dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.text.edits.ReplaceEdit;
699e6db060854d0e890190919a27a1846f50f69d1aTor Norbyeimport org.eclipse.text.edits.TextEdit;
709e6db060854d0e890190919a27a1846f50f69d1aTor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
718dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
729e6db060854d0e890190919a27a1846f50f69d1aTor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
738dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.w3c.dom.Attr;
748dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.w3c.dom.Element;
7578d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbyeimport org.w3c.dom.NamedNodeMap;
768dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.w3c.dom.Node;
778dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport org.w3c.dom.NodeList;
788dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
798dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.ArrayList;
808dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.HashSet;
818dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.List;
828dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.Map;
838dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyeimport java.util.Set;
848dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
858dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye/**
868dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye * Converts the selected layout into a layout of a different type.
878dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye */
888dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye@SuppressWarnings("restriction") // XML model
898dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbyepublic class ChangeLayoutRefactoring extends VisualRefactoring {
900757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    private static final String KEY_TYPE = "type";       //$NON-NLS-1$
910757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    private static final String KEY_FLATTEN = "flatten"; //$NON-NLS-1$
928dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
938dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private String mTypeFqcn;
941294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye    private String mInitializedAttributes;
950757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    private boolean mFlatten;
968dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
978dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /**
988dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * This constructor is solely used by {@link Descriptor},
998dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * to replay a previous refactoring.
1008dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param arguments argument map created by #createArgumentMap.
1018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     */
1028dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    ChangeLayoutRefactoring(Map<String, String> arguments) {
1038dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        super(arguments);
1048dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        mTypeFqcn = arguments.get(KEY_TYPE);
1050757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        mFlatten = Boolean.parseBoolean(arguments.get(KEY_FLATTEN));
1060757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    }
1070757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
1080757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    @VisibleForTesting
109e50549f54810c29bffc681e39d33158ab2a8f26eRaphael    ChangeLayoutRefactoring(List<Element> selectedElements, LayoutEditorDelegate delegate) {
110e50549f54810c29bffc681e39d33158ab2a8f26eRaphael        super(selectedElements, delegate);
1118dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1128dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
113e50549f54810c29bffc681e39d33158ab2a8f26eRaphael    public ChangeLayoutRefactoring(
114e50549f54810c29bffc681e39d33158ab2a8f26eRaphael            IFile file,
115e50549f54810c29bffc681e39d33158ab2a8f26eRaphael            LayoutEditorDelegate delegate,
116e50549f54810c29bffc681e39d33158ab2a8f26eRaphael            ITextSelection selection,
1178dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            ITreeSelection treeSelection) {
118e50549f54810c29bffc681e39d33158ab2a8f26eRaphael        super(file, delegate, selection, treeSelection);
1198dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
1228dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
1238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            OperationCanceledException {
1248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        RefactoringStatus status = new RefactoringStatus();
1258dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1268dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        try {
1278dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            pm.beginTask("Checking preconditions...", 2);
1288dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1298dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (mSelectionStart == -1 || mSelectionEnd == -1) {
1308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                status.addFatalError("No selection to convert");
1318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                return status;
1328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
1338dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1348dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (mElements.size() != 1) {
1358dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                status.addFatalError("Select precisely one layout to convert");
1368dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                return status;
1378dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
1388dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1398dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            pm.worked(1);
1408dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            return status;
1418dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1428dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } finally {
1438dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            pm.done();
1448dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
1458dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1468dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1478dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
1488dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    protected VisualRefactoringDescriptor createDescriptor() {
1498dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String comment = getName();
1508dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return new Descriptor(
1518dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                mProject.getName(), //project
1528dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                comment, //description
1538dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                comment, //comment
1548dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                createArgumentMap());
1558dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1568dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
1588dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    protected Map<String, String> createArgumentMap() {
1598dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Map<String, String> args = super.createArgumentMap();
1608dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        args.put(KEY_TYPE, mTypeFqcn);
1610757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        args.put(KEY_FLATTEN, Boolean.toString(mFlatten));
1628dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1638dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return args;
1648dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1658dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1668dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
1678dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    public String getName() {
1688dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return "Change Layout";
1698dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1708dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1718dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    void setType(String typeFqcn) {
1728dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        mTypeFqcn = typeFqcn;
1738dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
1748dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
1751294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye    void setInitializedAttributes(String initializedAttributes) {
1761294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye        mInitializedAttributes = initializedAttributes;
1771294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye    }
1781294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye
1790757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    void setFlatten(boolean flatten) {
1800757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        mFlatten = flatten;
1810757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    }
1820757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
1830757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    @Override
1840757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    protected List<Element> initElements() {
1850757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        List<Element> elements = super.initElements();
1860757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
1870757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        // Don't convert a root GestureOverlayView; convert its child. This looks for
1880757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        // gesture overlays, and if found, it generates a new child list where the gesture
1890757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        // overlay children are replaced by their first element children
1900757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        for (Element element : elements) {
1910757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            String tagName = element.getTagName();
1920757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            if (tagName.equals(GESTURE_OVERLAY_VIEW)
1930757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    || tagName.equals(FQCN_GESTURE_OVERLAY_VIEW)) {
1940757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                List<Element> replacement = new ArrayList<Element>(elements.size());
1950757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                for (Element e : elements) {
1960757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    tagName = e.getTagName();
1970757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    if (tagName.equals(GESTURE_OVERLAY_VIEW)
1980757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            || tagName.equals(FQCN_GESTURE_OVERLAY_VIEW)) {
1990757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        NodeList children = e.getChildNodes();
2000757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        Element first = null;
2010757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        for (int i = 0, n = children.getLength(); i < n; i++) {
2020757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            Node node = children.item(i);
2030757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            if (node.getNodeType() == Node.ELEMENT_NODE) {
2040757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                                first = (Element) node;
2050757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                                break;
2060757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            }
2070757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        }
2080757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        if (first != null) {
2090757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            e = first;
2100757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        }
2110757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    }
2120757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    replacement.add(e);
2130757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                }
2140757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                return replacement;
2150757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            }
2160757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        }
2170757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
2180757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        return elements;
2190757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    }
2200757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
2218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    @Override
2227e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) {
2238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String name = getViewClass(mTypeFqcn);
2248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
225e50549f54810c29bffc681e39d33158ab2a8f26eRaphael        IFile file = mDelegate.getEditor().getInputFile();
2268dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        List<Change> changes = new ArrayList<Change>();
227f14914e59366377316c70f068347d3e34a1dea3cTor Norbye        if (file == null) {
228f14914e59366377316c70f068347d3e34a1dea3cTor Norbye            return changes;
229f14914e59366377316c70f068347d3e34a1dea3cTor Norbye        }
2308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        TextFileChange change = new TextFileChange(file.getName(), file);
2318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        MultiTextEdit rootEdit = new MultiTextEdit();
2328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        change.setTextType(EXT_XML);
2338dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        changes.add(change);
2348dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2358dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String text = getText(mSelectionStart, mSelectionEnd);
2368dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Element layout = getPrimaryElement();
2378dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String oldName = layout.getNodeName();
2388dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        int open = text.indexOf(oldName);
2398dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        int close = text.lastIndexOf(oldName);
2408dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2418dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        if (open != -1 && close != -1) {
2428dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            int oldLength = oldName.length();
2438dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            rootEdit.addChild(new ReplaceEdit(mSelectionStart + open, oldLength, name));
2448dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (close != open) { // Gracefully handle <FooLayout/>
2458dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                rootEdit.addChild(new ReplaceEdit(mSelectionStart + close, oldLength, name));
2468dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
2478dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
2488dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
2499e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        String oldId = getId(layout);
2509e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        String newId = ensureIdMatchesType(layout, mTypeFqcn, rootEdit);
2519e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        // Update any layout references to the old id with the new id
2529e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        if (oldId != null && newId != null) {
253e50549f54810c29bffc681e39d33158ab2a8f26eRaphael            IStructuredModel model = mDelegate.getEditor().getModelForRead();
2549e6db060854d0e890190919a27a1846f50f69d1aTor Norbye            try {
2559e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                IStructuredDocument doc = model.getStructuredDocument();
2569e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                if (doc != null) {
2579e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                    List<TextEdit> replaceIds = replaceIds(getAndroidNamespacePrefix(), doc,
2589e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                            mSelectionStart,
2599e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                            mSelectionEnd, oldId, newId);
2609e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                    for (TextEdit edit : replaceIds) {
2619e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                        rootEdit.addChild(edit);
2629e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                    }
2639e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                }
2649e6db060854d0e890190919a27a1846f50f69d1aTor Norbye            } finally {
2659e6db060854d0e890190919a27a1846f50f69d1aTor Norbye                model.releaseFromRead();
2669e6db060854d0e890190919a27a1846f50f69d1aTor Norbye            }
2679e6db060854d0e890190919a27a1846f50f69d1aTor Norbye        }
2680757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
2698dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String oldType = getOldType();
2708dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String newType = mTypeFqcn;
27178d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye
2720757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        if (newType.equals(FQCN_RELATIVE_LAYOUT)) {
2730757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            if (oldType.equals(FQCN_LINEAR_LAYOUT) && !mFlatten) {
2740757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // Hand-coded conversion specifically tailored for linear to relative, provided
2750757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // there is no hierarchy flattening
2760757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // TODO: use the RelativeLayoutConversionHelper for this; it does a better job
2770757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // analyzing gravities etc.
2780757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                convertLinearToRelative(rootEdit);
27978d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                removeUndefinedAttrs(rootEdit, layout);
28078d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, null);
2810757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            } else {
2820757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // Generic conversion to relative - can also flatten the hierarchy
28378d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                convertAnyToRelative(rootEdit, oldType, newType);
2840757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                // This already handles removing undefined layout attributes -- right?
2850757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                //removeUndefinedLayoutAttrs(rootEdit, layout);
2860757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            }
287564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye        } else if (newType.equals(FQCN_GRID_LAYOUT)) {
288564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye            convertAnyToGridLayout(rootEdit);
2894cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            // Layout attributes on children have already been removed as part of conversion
2904cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            // during the flattening
2914cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            removeUndefinedAttrs(rootEdit, layout, false /*removeLayoutAttrs*/);
2928dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } else if (oldType.equals(FQCN_RELATIVE_LAYOUT) && newType.equals(FQCN_LINEAR_LAYOUT)) {
2938dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            convertRelativeToLinear(rootEdit);
29478d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            removeUndefinedAttrs(rootEdit, layout);
29578d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, null);
2968dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } else if (oldType.equals(FQCN_LINEAR_LAYOUT) && newType.equals(FQCN_TABLE_LAYOUT)) {
2978dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            convertLinearToTable(rootEdit);
29878d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            removeUndefinedAttrs(rootEdit, layout);
29978d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, null);
3008dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } else {
301564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye            convertGeneric(rootEdit, oldType, newType, layout);
3028dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
3038dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3041294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye        if (mInitializedAttributes != null && mInitializedAttributes.length() > 0) {
3051294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye            String namespace = getAndroidNamespacePrefix();
3061294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye            for (String s : mInitializedAttributes.split(",")) { //$NON-NLS-1$
3071294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                String[] nameValue = s.split("="); //$NON-NLS-1$
3081294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                String attribute = nameValue[0];
3091294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                String value = nameValue[1];
3101294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                String prefix = null;
3111294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                String namespaceUri = null;
31212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye                if (attribute.startsWith(SdkConstants.ANDROID_NS_NAME_PREFIX)) {
3131294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                    prefix = namespace;
3141294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                    namespaceUri = ANDROID_URI;
31512d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye                    attribute = attribute.substring(SdkConstants.ANDROID_NS_NAME_PREFIX.length());
3161294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                }
3171294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                setAttribute(rootEdit, layout, namespaceUri,
3181294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye                        prefix, attribute, value);
3191294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye            }
3201294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye        }
3211294048c3a602ab0de89f46b0f11cbb19d083befTor Norbye
322f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye        if (AdtPrefs.getPrefs().getFormatGuiXml()) {
323f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye            MultiTextEdit formatted = reformat(rootEdit, XmlFormatStyle.LAYOUT);
324f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye            if (formatted != null) {
325f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye                rootEdit = formatted;
326f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye            }
327f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye        }
328f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye        change.setEdit(rootEdit);
329f5256a38ef2e429b5efd4482808bc902bf7634e3Tor Norbye
3308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return changes;
3318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
3328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
33378d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye    /** Checks whether we need to add any missing attributes on the elements */
33478d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye    private void addMissingWrapContentAttributes(MultiTextEdit rootEdit, Element layout,
33578d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            String oldType, String newType, Set<Element> skip) {
33678d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        if (oldType.equals(FQCN_GRID_LAYOUT) && !newType.equals(FQCN_GRID_LAYOUT)) {
33778d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            String namespace = getAndroidNamespacePrefix();
33878d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye
33978d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            for (Element child : DomUtilities.getChildren(layout)) {
34078d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                if (skip != null && skip.contains(child)) {
34178d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                    continue;
34278d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                }
34378d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye
34478d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                if (!child.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH)) {
34578d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                    setAttribute(rootEdit, child, ANDROID_URI,
34678d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                            namespace, ATTR_LAYOUT_WIDTH, VALUE_WRAP_CONTENT);
34778d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                }
34878d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                if (!child.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT)) {
34978d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                    setAttribute(rootEdit, child, ANDROID_URI,
35078d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                            namespace, ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT);
35178d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                }
35278d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            }
35378d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        }
35478d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye    }
35578d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye
3568dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /** Hand coded conversion from a LinearLayout to a TableLayout */
3578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void convertLinearToTable(MultiTextEdit rootEdit) {
3588dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // This is pretty easy; just switch the root tag (already done by the initial generic
3598dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // conversion) and then convert all the children into <TableRow> elements.
3608dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // Finally, get rid of the orientation attribute, if any.
3618dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Element layout = getPrimaryElement();
3628dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        removeOrientationAttribute(rootEdit, layout);
3638dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3648dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        NodeList children = layout.getChildNodes();
3658dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        for (int i = 0, n = children.getLength(); i < n; i++) {
3668dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            Node node = children.item(i);
3678dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (node.getNodeType() == Node.ELEMENT_NODE) {
3688dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Element child = (Element) node;
3698dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                if (node instanceof IndexedRegion) {
3708dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    IndexedRegion region = (IndexedRegion) node;
3718dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    int start = region.getStartOffset();
3728dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    int end = region.getEndOffset();
3738dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    String text = getText(start, end);
3748dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    String oldName = child.getNodeName();
3758dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    if (oldName.equals(LINEAR_LAYOUT)) {
3768dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        removeOrientationAttribute(rootEdit, child);
3778dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        int open = text.indexOf(oldName);
3788dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        int close = text.lastIndexOf(oldName);
3798dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3808dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        if (open != -1 && close != -1) {
3818dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                            int oldLength = oldName.length();
3828dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                            rootEdit.addChild(new ReplaceEdit(mSelectionStart + open, oldLength,
3838dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                    TABLE_ROW));
3848dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                            if (close != open) { // Gracefully handle <FooLayout/>
3858dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                rootEdit.addChild(new ReplaceEdit(mSelectionStart + close,
3868dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                        oldLength, TABLE_ROW));
3878dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                            }
3888dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        }
3898dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    } // else: WRAP in TableLayout!
3908dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                }
3918dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
3928dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
3938dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
3948dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
3958dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     /** Hand coded conversion from a LinearLayout to a RelativeLayout */
3968dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void convertLinearToRelative(MultiTextEdit rootEdit) {
3978dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // This can be done accurately.
3988dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Element layout = getPrimaryElement();
3998dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // Horizontal is the default, so if no value is specified it is horizontal.
4008dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        boolean isVertical = VALUE_VERTICAL.equals(layout.getAttributeNS(ANDROID_URI,
4018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                ATTR_ORIENTATION));
4028dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4038dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        String attributePrefix = getAndroidNamespacePrefix();
4048dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4058dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Consider gravity of each element
4068dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Consider weight of each element
4078dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // Right now it simply makes a single attachment to keep the order.
4088dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4098dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        if (isVertical) {
4108dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            // Align each child to the bottom and left of its parent
4118dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            NodeList children = layout.getChildNodes();
4128dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            String prevId = null;
4138dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            for (int i = 0, n = children.getLength(); i < n; i++) {
4148dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Node node = children.item(i);
4158dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                if (node.getNodeType() == Node.ELEMENT_NODE) {
4168dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    Element child = (Element) node;
4170757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    String id = ensureHasId(rootEdit, child, null);
4188dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    if (prevId != null) {
4190757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
4208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                ATTR_LAYOUT_BELOW, prevId);
4218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    }
4228dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    prevId = id;
4238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                }
4248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
4258dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        } else {
4268dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            // Align each child to the left
4278dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            NodeList children = layout.getChildNodes();
4288dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            boolean isBaselineAligned =
4298dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                !VALUE_FALSE.equals(layout.getAttributeNS(ANDROID_URI, ATTR_BASELINE_ALIGNED));
4308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            String prevId = null;
4328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            for (int i = 0, n = children.getLength(); i < n; i++) {
4338dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Node node = children.item(i);
4348dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                if (node.getNodeType() == Node.ELEMENT_NODE) {
4358dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    Element child = (Element) node;
4360757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                    String id = ensureHasId(rootEdit, child, null);
4378dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    if (prevId != null) {
4380757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                        setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
4398dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                ATTR_LAYOUT_TO_RIGHT_OF, prevId);
4408dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        if (isBaselineAligned) {
4410757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                            setAttribute(rootEdit, child, ANDROID_URI, attributePrefix,
4428dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                                    ATTR_LAYOUT_ALIGN_BASELINE, prevId);
4438dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                        }
4448dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    }
4458dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    prevId = id;
4468dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                }
4478dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
4488dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
4498dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
4508dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4518dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /** Strips out the android:orientation attribute from the given linear layout element */
4528dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void removeOrientationAttribute(MultiTextEdit rootEdit, Element layout) {
4538dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        assert layout.getTagName().equals(LINEAR_LAYOUT);
4548dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        removeAttribute(rootEdit, layout, ANDROID_URI, ATTR_ORIENTATION);
4558dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
4568dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /**
4588dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * Hand coded conversion from a RelativeLayout to a LinearLayout
4598dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     *
4608dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param rootEdit the root multi text edit to add edits to
4618dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     */
4628dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    private void convertRelativeToLinear(MultiTextEdit rootEdit) {
4638dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // This is going to be lossy...
4648dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Attempt to "order" the items based on their visual positions
4658dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // and insert them in that order in the LinearLayout.
4668dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Possibly use nesting if necessary, by spatial subdivision,
4678dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // to accomplish roughly the same layout as the relative layout specifies.
4688dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
4698dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
4708dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    /**
4718dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * Hand coded -generic- conversion from one layout to another. This is not going to be
4728dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * an accurate layout transformation; instead it simply migrates the layout attributes
4738dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * that are supported, and adds defaults for any new required layout attributes. In
4748dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * addition, it attempts to order the children visually based on where they fit in a
4758dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * rendering. (Unsupported layout attributes will be removed by the caller at the
4768dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * end.)
4778dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * <ul>
4788dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * <li>Try to handle nesting. Converting a *hierarchy* of layouts into a flatter
4798dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * layout for powerful layouts that support it, like RelativeLayout.
4808dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * <li>Try to do automatic "inference" about the layout. I can render it and look at
4818dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * the ViewInfo positions and sizes. I can render it multiple times, at different
4828dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * sizes, to infer "stretchiness" and "weight" properties of the children.
4838dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * <li>Try to do indirect transformations. E.g. if I can go from A to B, and B to C,
4848dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * then an attempt to go from A to C should perform conversions A to B and then B to
4858dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * C.
4868dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * </ul>
4878dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     *
4888dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param rootEdit the root multi text edit to add edits to
4898dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param oldType the fully qualified class name of the layout type we are converting
4908dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     *            from
4918dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     * @param newType the fully qualified class name of the layout type we are converting
4928dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     *            to
493564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye     * @param layout the layout to be converted
4948dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye     */
495564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye    private void convertGeneric(MultiTextEdit rootEdit, String oldType, String newType,
496564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye            Element layout) {
4978dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // TODO: Add hooks for 3rd party conversions getting registered through the
4988dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // IViewRule interface.
4998dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
5008dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // For now we simply go with the default behavior, which is to just strip the
5018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        // layout attributes that aren't supported.
50278d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        removeUndefinedAttrs(rootEdit, layout);
50378d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, null);
5048dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
5058dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
50678d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye    /**
50778d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye     * Removes all the unavailable attributes after a conversion, both on the
50878d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye     * layout element itself as well as the layout attributes of any of the
50978d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye     * children
51078d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye     */
51178d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye    private void removeUndefinedAttrs(MultiTextEdit rootEdit, Element layout) {
5124cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye        removeUndefinedAttrs(rootEdit, layout, true /*removeLayoutAttrs*/);
5134cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye    }
5144cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye
5154cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye    private void removeUndefinedAttrs(MultiTextEdit rootEdit, Element layout,
5164cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            boolean removeLayoutAttrs) {
5170757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        ViewElementDescriptor descriptor = getElementDescriptor(mTypeFqcn);
5188dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        if (descriptor == null) {
5198dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            return;
5208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
5218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
5224cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye        if (removeLayoutAttrs) {
5234cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            Set<String> defined = new HashSet<String>();
5244cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            AttributeDescriptor[] layoutAttributes = descriptor.getLayoutAttributes();
5254cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            for (AttributeDescriptor attribute : layoutAttributes) {
5264cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                defined.add(attribute.getXmlLocalName());
5274cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            }
5288dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
5294cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            NodeList children = layout.getChildNodes();
5304cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye            for (int i = 0, n = children.getLength(); i < n; i++) {
5314cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                Node node = children.item(i);
5324cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                if (node.getNodeType() == Node.ELEMENT_NODE) {
5334cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                    Element child = (Element) node;
5348dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
5354cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                    List<Attr> attributes = findLayoutAttributes(child);
5364cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                    for (Attr attribute : attributes) {
5374cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                        String name = attribute.getLocalName();
5384cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                        if (!defined.contains(name)) {
5394cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                            // Remove it
5404cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                            try {
5414cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                                removeAttribute(rootEdit, child, attribute.getNamespaceURI(), name);
5424cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                            } catch (MalformedTreeException mte) {
5434cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                                // Sometimes refactoring has modified attribute; not removing
5444cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                                // it is non-fatal so just warn instead of letting refactoring
5454cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                                // operation abort
5464cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                                AdtPlugin.log(IStatus.WARNING,
5474cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                                        "Could not remove unsupported attribute %1$s; " + //$NON-NLS-1$
5484cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                                        "already modified during refactoring?", //$NON-NLS-1$
5494cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                                        attribute.getLocalName());
5504cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                            }
5514cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye                        }
5528dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    }
5538dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                }
5548dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
5558dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
55678d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye
55778d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        // Also remove the unavailable attributes (not layout attributes) on the
55878d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        // converted element
5594cd282c7b21dc06c2d2d02748278f07c94282fc1Tor Norbye        Set<String> defined = new HashSet<String>();
56078d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        AttributeDescriptor[] attributes = descriptor.getAttributes();
56178d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        for (AttributeDescriptor attribute : attributes) {
56278d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            defined.add(attribute.getXmlLocalName());
56378d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        }
56478d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye
56578d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        // Remove undefined attributes on the layout element itself
56678d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        NamedNodeMap attributeMap = layout.getAttributes();
56778d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        for (int i = 0, n = attributeMap.getLength(); i < n; i++) {
56878d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            Node attributeNode = attributeMap.item(i);
56978d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye
57078d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            String name = attributeNode.getLocalName();
57112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye            if (!name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)
57278d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                    && ANDROID_URI.equals(attributeNode.getNamespaceURI())) {
57378d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                if (!defined.contains(name)) {
57478d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                    // Remove it
57578d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                    removeAttribute(rootEdit, layout, ANDROID_URI, name);
57678d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye                }
57778d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            }
57878d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        }
5798dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
5808dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
5810757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    /** Hand coded conversion from any layout to a RelativeLayout */
58278d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye    private void convertAnyToRelative(MultiTextEdit rootEdit, String oldType, String newType) {
5830757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        // To perform a conversion from any other layout type, including nested conversion,
5840757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        Element layout = getPrimaryElement();
5850757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        CanvasViewInfo rootView = mRootView;
5860757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        if (rootView == null) {
587e50549f54810c29bffc681e39d33158ab2a8f26eRaphael            LayoutCanvas canvas = mDelegate.getGraphicalEditor().getCanvasControl();
5880757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            ViewHierarchy viewHierarchy = canvas.getViewHierarchy();
5890757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            rootView = viewHierarchy.getRoot();
5908dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
5918dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
5920757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        RelativeLayoutConversionHelper helper =
5930757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye            new RelativeLayoutConversionHelper(this, layout, mFlatten, rootEdit, rootView);
5940757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        helper.convertToRelative();
59578d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        List<Element> deletedElements = helper.getDeletedElements();
59678d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        Set<Element> deleted = null;
59778d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        if (deletedElements != null && deletedElements.size() > 0) {
59878d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye            deleted = new HashSet<Element>(deletedElements);
59978d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        }
60078d3947aeebf2d8c0d17fe35d0fd94e53013b60eTor Norbye        addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, deleted);
6018dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
6028dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
603564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye    /** Hand coded conversion from any layout to a GridLayout */
604564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye    private void convertAnyToGridLayout(MultiTextEdit rootEdit) {
605564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye        // To perform a conversion from any other layout type, including nested conversion,
606564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye        Element layout = getPrimaryElement();
607564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye        CanvasViewInfo rootView = mRootView;
608564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye        if (rootView == null) {
609e50549f54810c29bffc681e39d33158ab2a8f26eRaphael            LayoutCanvas canvas = mDelegate.getGraphicalEditor().getCanvasControl();
610564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye            ViewHierarchy viewHierarchy = canvas.getViewHierarchy();
611564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye            rootView = viewHierarchy.getRoot();
612564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye        }
613564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye
614564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye        GridLayoutConverter converter = new GridLayoutConverter(this, layout, mFlatten,
615564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye                rootEdit, rootView);
616564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye        converter.convertToGridLayout();
617564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye    }
618564e4b80f642af7fa13c01f407c112778e25ae1eTor Norbye
6198dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    public static class Descriptor extends VisualRefactoringDescriptor {
6208dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        public Descriptor(String project, String description, String comment,
6218dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                Map<String, String> arguments) {
6228dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            super("com.android.ide.eclipse.adt.refactoring.convert", //$NON-NLS-1$
6238dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                    project, description, comment, arguments);
6248dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
6258dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
6268dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        @Override
6278dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        protected Refactoring createRefactoring(Map<String, String> args) {
6288dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            return new ChangeLayoutRefactoring(args);
6298dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
6308dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
6318dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
6328dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    String getOldType() {
6338dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        Element primary = getPrimaryElement();
6348dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        if (primary != null) {
6358dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            String oldType = primary.getTagName();
6368dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            if (oldType.indexOf('.') == -1) {
6378dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye                oldType = ANDROID_WIDGET_PREFIX + oldType;
6388dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            }
6398dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye            return oldType;
6408dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        }
6418dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye
6428dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye        return null;
6438dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye    }
6440757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
6450757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    @VisibleForTesting
6460757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    protected CanvasViewInfo mRootView;
6470757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye
6480757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    @VisibleForTesting
6490757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    public void setRootView(CanvasViewInfo rootView) {
6500757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye        mRootView = rootView;
6510757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye    }
652891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye
653891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye    @Override
654891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye    VisualRefactoringWizard createWizard() {
655e50549f54810c29bffc681e39d33158ab2a8f26eRaphael        return new ChangeLayoutWizard(this, mDelegate);
656891fe684c31864eab0ef4372ea6379f2c3b5fb4fTor Norbye    }
6578dc4366bbaad39d56e1c2ded4046c86a95a17666Tor Norbye}
658