aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java
blob: be06d38e2e158eeeaaa1c0bc3bff56ba82cfb293 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.eclipse.org/org/documents/epl-v10.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ide.eclipse.adt.internal.editors.common;

import com.android.ide.common.resources.ResourceFolder;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.animator.AnimationEditorDelegate;
import com.android.ide.eclipse.adt.internal.editors.color.ColorEditorDelegate;
import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlDelegate.IDelegateCreator;
import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableEditorDelegate;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
import com.android.ide.eclipse.adt.internal.editors.menu.MenuEditorDelegate;
import com.android.ide.eclipse.adt.internal.editors.otherxml.OtherXmlEditorDelegate;
import com.android.ide.eclipse.adt.internal.editors.otherxml.PlainXmlEditorDelegate;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.editors.values.ValuesEditorDelegate;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.resources.ResourceFolderType;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension2;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IShowEditorInput;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.ide.IDE;
import org.w3c.dom.Document;

/**
 * Multi-page form editor for ALL /res XML files.
 * <p/>
 * This editor doesn't actually do anything. Instead, it defers actual implementation
 * to {@link CommonXmlDelegate} instances.
 */
public class CommonXmlEditor extends AndroidXmlEditor implements IShowEditorInput {

    public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".CommonXmlEditor"; //$NON-NLS-1$

    /**
     * Registered {@link CommonXmlDelegate}s.
     * All delegates must have a {@code Creator} class which is instantiated
     * once here statically. All the creators are invoked in the order they
     * are defined and the first one to return a non-null delegate is used.
     */
    private static final IDelegateCreator[] DELEGATES = {
            new LayoutEditorDelegate.Creator(),
            new ValuesEditorDelegate.Creator(),
            new AnimationEditorDelegate.Creator(),
            new ColorEditorDelegate.Creator(),
            new DrawableEditorDelegate.Creator(),
            new MenuEditorDelegate.Creator(),
            new OtherXmlEditorDelegate.Creator(),
    };

    /**
     * IDs of legacy editors replaced by the {@link CommonXmlEditor}.
     */
    public static final String[] LEGACY_EDITOR_IDS = {
        LayoutEditorDelegate.LEGACY_EDITOR_ID,
        ValuesEditorDelegate.LEGACY_EDITOR_ID,
        AnimationEditorDelegate.LEGACY_EDITOR_ID,
        ColorEditorDelegate.LEGACY_EDITOR_ID,
        DrawableEditorDelegate.LEGACY_EDITOR_ID,
        MenuEditorDelegate.LEGACY_EDITOR_ID,
        OtherXmlEditorDelegate.LEGACY_EDITOR_ID,
    };

    private CommonXmlDelegate mDelegate = null;

    /**
     * Creates the form editor for resources XML files.
     */
    public CommonXmlEditor() {
        super();
    }

    @Override
    public void init(IEditorSite site, final IEditorInput editorInput)
            throws PartInitException {
        if (editorInput instanceof IFileEditorInput) {

            IFileEditorInput fileInput = (IFileEditorInput) editorInput;
            IFile file = fileInput.getFile();

            // Adjust the default file editor ID

            IEditorDescriptor file_desc = IDE.getDefaultEditor(file);
            String id = file_desc == null ? null : file_desc.getId();
            boolean mustChange = id != null &&
                                 !id.equals(ID) &&
                                 id.startsWith(AdtConstants.EDITORS_NAMESPACE);
            if (!mustChange) {
                // Maybe this was opened by a manual Open With with a legacy ID?
                id = site.getId();
                mustChange = id != null &&
                             !id.equals(ID) &&
                             id.startsWith(AdtConstants.EDITORS_NAMESPACE);
            }

            if (mustChange) {
                // It starts by our editor namespace but it's not the right ID.
                // This is an old Android XML ID. Change it to our new ID.
                IDE.setDefaultEditor(file, ID);
                AdtPlugin.log(IStatus.INFO,
                        "Changed legacy editor ID %s for %s",   //$NON-NLS-1$
                        id,
                        file.getFullPath());
            }

            // Now find the delegate for the file.

            ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
            ResourceFolderType type = resFolder == null ? null : resFolder.getType();

            if (type == null) {
                // We lack any real resource information about that file.
                // Let's take a guess using the actual path.
                String folderName = AdtUtils.getParentFolderName(editorInput);
                type = ResourceFolderType.getFolderType(folderName);
            }

            if (type != null) {
                for (IDelegateCreator creator : DELEGATES) {
                    mDelegate = creator.createForFile(this, type);
                    if (mDelegate != null) {
                        break;
                    }
                }
            }

            if (mDelegate == null) {
                // We didn't find any editor.
                // We'll use the PlainXmlEditorDelegate as a catch-all editor.
                AdtPlugin.log(IStatus.INFO,
                        "No valid Android XML Editor Delegate found for file %1$s [Res %2$s, type %3$s]",
                        file.getFullPath(),
                        resFolder,
                        type);
                mDelegate = new PlainXmlEditorDelegate(this);
            }
        } else if (editorInput instanceof IURIEditorInput) {
            String folderName = AdtUtils.getParentFolderName(editorInput);
            ResourceFolderType type = ResourceFolderType.getFolderType(folderName);
            if (type == ResourceFolderType.LAYOUT) {
                // The layout editor has a lot of hardcoded requirements for real IFiles
                // and IProjects so for now just use a plain XML editor for project-less layout
                // files
                mDelegate = new OtherXmlEditorDelegate(this);
            } else if (type != null) {
                for (IDelegateCreator creator : DELEGATES) {
                    mDelegate = creator.createForFile(this, type);
                    if (mDelegate != null) {
                        break;
                    }
                }
            }

            if (mDelegate == null) {
                // We didn't find any editor.
                // We'll use the PlainXmlEditorDelegate as a catch-all editor.
                AdtPlugin.log(IStatus.INFO,
                        "No valid Android XML Editor Delegate found for file %1$s [Res %2$s, type %3$s]",
                        ((IURIEditorInput) editorInput).getURI().toString(),
                        folderName,
                        type);
                mDelegate = new PlainXmlEditorDelegate(this);
            }
        }

        if (mDelegate == null) {
            // We can't do anything if we don't have a valid file.
            AdtPlugin.log(IStatus.INFO,
                    "Android XML Editor cannot process non-file input %1$s",   //$NON-NLS-1$
                    (editorInput == null ? "null" : editorInput.toString()));   //$NON-NLS-1$
            throw new PartInitException("Android XML Editor cannot process this input.");
        } else {
            // Invoke the editor's init after setting up the delegate. This will call setInput().
            super.init(site, editorInput);
        }
    }

    /**
     * @return The root node of the UI element hierarchy
     */
    @Override
    public UiElementNode getUiRootNode() {
        return mDelegate == null ? null : mDelegate.getUiRootNode();
    }

    public CommonXmlDelegate getDelegate() {
        return mDelegate;
    }

    // ---- Base Class Overrides ----

    @Override
    public void dispose() {
        if (mDelegate != null) {
            mDelegate.dispose();
        }

        super.dispose();
    }

    /**
     * Save the XML.
     * <p/>
     * The actual save operation is done in the super class by committing
     * all data to the XML model and then having the Structured XML Editor
     * save the XML.
     * <p/>
     * Here we just need to tell the delegate that the model has
     * been saved.
     */
    @Override
    public void doSave(IProgressMonitor monitor) {
        super.doSave(monitor);
        if (mDelegate != null) {
            mDelegate.delegateDoSave(monitor);
        }
    }

    /**
     * Returns whether the "save as" operation is supported by this editor.
     * <p/>
     * Save-As is a valid operation for the ManifestEditor since it acts on a
     * single source file.
     *
     * @see IEditorPart
     */
    @Override
    public boolean isSaveAsAllowed() {
        return mDelegate == null ? false : mDelegate.isSaveAsAllowed();
    }

    /**
     * Create the various form pages.
     */
    @Override
    protected void createFormPages() {
        if (mDelegate != null) {
            mDelegate.delegateCreateFormPages();
        }
    }

    @Override
    protected void postCreatePages() {
        super.postCreatePages();

        if (mDelegate != null) {
            mDelegate.delegatePostCreatePages();
        }
    }

    @Override
    protected void addPages() {
        // Create the editor pages.
        // This will also create the EditorPart.
        super.addPages();

        // When the EditorPart is being created, it configures the SourceViewer
        // and will try to use our CommonSourceViewerConfig. Our config needs to
        // know which ContentAssist processor to use (since we have one per resource
        // folder type) but it doesn't have the necessary info to do so.
        // Consequently, once the part is created, we can now unconfigure the source
        // viewer and reconfigure it with the right settings.
        ISourceViewer ssv = getStructuredSourceViewer();
        if (mDelegate != null && ssv instanceof ISourceViewerExtension2) {
            ((ISourceViewerExtension2) ssv).unconfigure();
            ssv.configure(new CommonSourceViewerConfig(
                    mDelegate.getAndroidContentAssistProcessor()));
        }
    }

    /* (non-java doc)
     * Change the tab/title name to include the name of the layout.
     */
    @Override
    protected void setInput(IEditorInput input) {
        super.setInput(input);
        assert mDelegate != null;
        if (mDelegate != null) {
            mDelegate.delegateSetInput(input);
        }
    }

    @Override
    public void setInputWithNotify(IEditorInput input) {
        super.setInputWithNotify(input);
        if (mDelegate instanceof LayoutEditorDelegate) {
            ((LayoutEditorDelegate) mDelegate).delegateSetInputWithNotify(input);
        }
    }

    /**
     * Processes the new XML Model, which XML root node is given.
     *
     * @param xml_doc The XML document, if available, or null if none exists.
     */
    @Override
    protected void xmlModelChanged(Document xml_doc) {
        if (mDelegate != null) {
            mDelegate.delegateXmlModelChanged(xml_doc);
        }
    }

    @Override
    protected Job runLint() {
        if (mDelegate != null && getEditorInput() instanceof IFileEditorInput) {
            return mDelegate.delegateRunLint();
        }
        return null;
    }

    /**
     * Returns the custom IContentOutlinePage or IPropertySheetPage when asked for it.
     */
    @Override
    public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
        if (mDelegate != null) {
            Object value = mDelegate.delegateGetAdapter(adapter);
            if (value != null) {
                return value;
            }
        }

        // return default
        return super.getAdapter(adapter);
    }

    @Override
    protected void pageChange(int newPageIndex) {
        if (mDelegate != null) {
            mDelegate.delegatePageChange(newPageIndex);
        }

        super.pageChange(newPageIndex);

        if (mDelegate != null) {
            mDelegate.delegatePostPageChange(newPageIndex);
        }
    }

    @Override
    protected int getPersistenceCategory() {
        if (mDelegate != null) {
            return mDelegate.delegateGetPersistenceCategory();
        }
        return CATEGORY_OTHER;
    }

    @Override
    public void initUiRootNode(boolean force) {
        if (mDelegate != null) {
            mDelegate.delegateInitUiRootNode(force);
        }
    }

    @Override
    public IFormPage setActivePage(String pageId) {
        IFormPage page = super.setActivePage(pageId);

        if (mDelegate != null) {
            return mDelegate.delegatePostSetActivePage(page, pageId);
        }

        return page;
    }

    /* Implements showEditorInput(...) in IShowEditorInput */
    @Override
    public void showEditorInput(IEditorInput editorInput) {
        if (mDelegate instanceof LayoutEditorDelegate) {
            ((LayoutEditorDelegate) mDelegate).showEditorInput(editorInput);
        }
    }

    @Override
    public boolean supportsFormatOnGuiEdit() {
        if (mDelegate != null) {
            return mDelegate.delegateSupportsFormatOnGuiEdit();
        }
        return super.supportsFormatOnGuiEdit();
    }

    @Override
    public void activated() {
        super.activated();
        if (mDelegate != null) {
            mDelegate.delegateActivated();
        }
    }

    @Override
    public void deactivated() {
        super.deactivated();
        if (mDelegate != null) {
            mDelegate.delegateDeactivated();
        }
    }

    @Override
    public String getPartName() {
        if (mDelegate != null) {
            String name = mDelegate.delegateGetPartName();
            if (name != null) {
                return name;
            }
        }

        return super.getPartName();
    }

    // --------------------
    // Base methods exposed so that XmlEditorDelegate can access them

    @Override
    public void setPartName(String partName) {
        super.setPartName(partName);
    }

    @Override
    public void setPageText(int pageIndex, String text) {
        super.setPageText(pageIndex, text);
    }

    @Override
    public void firePropertyChange(int propertyId) {
        super.firePropertyChange(propertyId);
    }

    @Override
    public int getPageCount() {
        return super.getPageCount();
    }

    @Override
    public int getCurrentPage() {
        return super.getCurrentPage();
    }
}