blob: be06d38e2e158eeeaaa1c0bc3bff56ba82cfb293 [file] [log] [blame]
/*
* 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();
}
}