blob: 478db28b964064f3ad427dfd6d25b9728d1651e2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2012 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.ui.internal.editors;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jpt.common.core.JptResourceType;
import org.eclipse.jpt.common.ui.WidgetFactory;
import org.eclipse.jpt.common.ui.internal.util.SWTUtil;
import org.eclipse.jpt.common.ui.internal.widgets.FormWidgetFactory;
import org.eclipse.jpt.common.utility.internal.ObjectTools;
import org.eclipse.jpt.common.utility.internal.StringBuilderTools;
import org.eclipse.jpt.common.utility.internal.model.value.CollectionAspectAdapter;
import org.eclipse.jpt.common.utility.internal.model.value.CollectionPropertyValueModelAdapter;
import org.eclipse.jpt.common.utility.internal.model.value.DoublePropertyValueModel;
import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel;
import org.eclipse.jpt.common.utility.internal.model.value.TransformationPropertyValueModel;
import org.eclipse.jpt.common.utility.internal.transformer.AbstractTransformer;
import org.eclipse.jpt.common.utility.iterable.ListIterable;
import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter;
import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener;
import org.eclipse.jpt.common.utility.model.value.CollectionValueModel;
import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel;
import org.eclipse.jpt.common.utility.model.value.PropertyValueModel;
import org.eclipse.jpt.common.utility.transformer.Transformer;
import org.eclipse.jpt.jpa.core.JpaFile;
import org.eclipse.jpt.jpa.core.JpaPlatform;
import org.eclipse.jpt.jpa.core.JpaStructureNode;
import org.eclipse.jpt.jpa.ui.JpaFileModel;
import org.eclipse.jpt.jpa.ui.JpaPlatformUi;
import org.eclipse.jpt.jpa.ui.ResourceUiDefinition;
import org.eclipse.jpt.jpa.ui.editors.JpaEditorPageDefinition;
import org.eclipse.jpt.jpa.ui.internal.JptUiMessages;
import org.eclipse.jpt.jpa.ui.internal.plugin.JptJpaUiPlugin;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.part.MultiPageEditorSite;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
import org.eclipse.wst.sse.ui.StructuredTextEditor;
/**
* This is an implementation of a multi-page editor that includes an
* XML source editor source tab as the last tab. The other tabs
* are defined by {@link ResourceUiDefinition#getEditorPageDefinitions()}
* The ResourceUiDefinition is found from the <code>JptResourceType</code> of the
* <code>JpaFile</code>
*
* @version 3.3
* @since 3.3
*/
public class JpaXmlEditor
extends FormEditor
{
/**
* The IFileEditorInput model
*/
private final ModifiablePropertyValueModel<IFileEditorInput> editorInputModel = new SimplePropertyValueModel<IFileEditorInput>();
/**
* The one we listen to
*/
private PropertyValueModel<JpaStructureNode> rootStructureNodeModel;
private final PropertyChangeListener rootStructureNodeListener = new RootStructureNodeListener();
/**
* The one to be passed to the Page
*/
private ModifiablePropertyValueModel<JpaStructureNode> pageRootStructureNodeModel;
/**
* The XML source text editor.
*/
private final StructuredTextEditor structuredTextEditor;
/**
* The factory used to create the various widgets.
*/
private final WidgetFactory widgetFactory;
/**
* The local resource manager, used to create/destroy Images
*/
final ResourceManager localResourceManager;
public JpaXmlEditor() {
super();
this.localResourceManager = new LocalResourceManager(JFaceResources.getResources());
this.widgetFactory = this.buildWidgetFactory();
this.structuredTextEditor = new StructuredTextEditor();
this.structuredTextEditor.setEditorPart(this);
}
private WidgetFactory buildWidgetFactory() {
return new FormWidgetFactory(
new TabbedPropertySheetWidgetFactory()
);
}
@Override
public void init(IEditorSite site, IEditorInput editorInput) throws PartInitException {
Assert.isLegal(editorInput instanceof IFileEditorInput, "Invalid Input: Must be IFileEditorInput"); //$NON-NLS-1$
super.init(site, editorInput);
this.setPartName(editorInput.getName());
this.rootStructureNodeModel = this.buildRootStructureNodeModel();
this.rootStructureNodeModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.rootStructureNodeListener);
}
@Override
protected void setInput(IEditorInput editorInput) {
super.setInput(editorInput);
this.editorInputModel.setValue((IFileEditorInput) editorInput);
}
@Override
protected void setInputWithNotify(IEditorInput editorInput) {
super.setInputWithNotify(editorInput);
this.editorInputModel.setValue((IFileEditorInput) editorInput);
}
@Override
protected void addPages() {
this.addXMLSourceEditorPage();
//TODO this.addXMLDesignEditorPage();
if (this.getRootStructureNode() != null) {
this.setPageRootStructureNode_(this.getRootStructureNode());
this.setActivePage(0);
}
}
/**
* Adds the page containing the XML source editor.
*/
protected void addXMLSourceEditorPage() {
try {
int index = this.addPage(this.structuredTextEditor, this.getEditorInput());
this.setPageText(index, JptUiMessages.JpaXmlEditor_sourcePage);
}
catch (PartInitException e) {
JptJpaUiPlugin.instance().logError(e);
}
}
/**
* Add the pages for editing the selected JpaStructureNode. These
* will be the pages that come before the XML source editor.
* <p>
* @see #getRootStructureNode()
* @see Page
*/
protected void addSpecificPages() {
JptResourceType resourceType = this.getRootStructureNode().getResourceType();
if (resourceType == null) {
return; // might not ever get here... (if we have a p.xml, it probably has a resource type...)
}
JpaPlatform jpaPlatform = this.getRootStructureNode().getJpaPlatform();
JpaPlatformUi jpaPlatformUI = (JpaPlatformUi) jpaPlatform.getAdapter(JpaPlatformUi.class);
ResourceUiDefinition definition = jpaPlatformUI.getResourceUiDefinition(resourceType);
ListIterable<JpaEditorPageDefinition> pageDefinitions = definition.getEditorPageDefinitions();
for (JpaEditorPageDefinition editorPageDefinition : pageDefinitions) {
FormPage formPage = new Page(editorPageDefinition);
int index = this.getPageCount() == 0 ? 0 : this.getPageCount() - 1;//always keep the source tab as the last tab
try {
this.addPage(index, formPage);
}
catch (PartInitException e) {
JptJpaUiPlugin.instance().logError(e);
}
}
}
@Override
protected IEditorSite createSite(IEditorPart editor) {
if (editor == this.structuredTextEditor) {
return new MultiPageEditorSite(this, editor) {
@Override
public String getId() {
// sets this id so nested editor is considered an xml source page
// I know this makes the XML source toolbar buttons appear on all
// the tabs instead of just the Source tab, not sure what else it does ~kfb
return "org.eclipse.core.runtime.xml.source"; //$NON-NLS-1$;
}
};
}
return super.createSite(editor);
}
/**
* Delegate to the {@link #structuredTextEditor} if necessary.
*/
@Override
public Object getAdapter(@SuppressWarnings("rawtypes") Class adapterClass) {
Object adapter = super.getAdapter(adapterClass);
return (adapter != null) ? adapter : this.structuredTextEditor.getAdapter(adapterClass);
}
@Override
public void doSave(IProgressMonitor monitor) {
this.structuredTextEditor.doSave(monitor);
}
@Override
public boolean isSaveAsAllowed() {
return false;
}
@Override
public void doSaveAs() {
// do nothing
}
// ********** JPA file **********
private PropertyValueModel<JpaFile> buildJpaFileModel() {
return new DoublePropertyValueModel<JpaFile>(this.buildJpaFileModelModel());
}
private PropertyValueModel<PropertyValueModel<JpaFile>> buildJpaFileModelModel() {
return new TransformationPropertyValueModel<IFileEditorInput, PropertyValueModel<JpaFile>>(this.editorInputModel, JPA_FILE_MODEL_TRANSFORMER);
}
private static final Transformer<IFileEditorInput, PropertyValueModel<JpaFile>> JPA_FILE_MODEL_TRANSFORMER = new JpaFileModelTransformer();
/* CU private */ static class JpaFileModelTransformer
extends AbstractTransformer<IFileEditorInput, PropertyValueModel<JpaFile>>
{
@Override
protected PropertyValueModel<JpaFile> transform_(IFileEditorInput fileEditorInput) {
return (JpaFileModel) fileEditorInput.getFile().getAdapter(JpaFileModel.class);
}
}
/* CU private */ class RootStructureNodeListener
extends PropertyChangeAdapter
{
@Override
public void propertyChanged(PropertyChangeEvent event) {
JpaXmlEditor.this.setPageRootStructureNode((JpaStructureNode) event.getNewValue());
}
}
/* CU private */ void setPageRootStructureNode(JpaStructureNode jpaStructureNode) {
this.execute(new SetPageRootStructureNodeRunnable(jpaStructureNode));
}
protected void setPageRootStructureNode_(JpaStructureNode rootStructureNode) {
if (this.pageRootStructureNodeModel != null) {
this.pageRootStructureNodeModel.setValue(null);
this.pageRootStructureNodeModel = null;
}
while (this.getPageCount() > 1) {//don't remove the XML Editor page
//set the XML source editor to be the active page before removing the other pages.
//If I don't do this and the active page gets removed it will build the contents
//of the next page. I don't want to do this since I'm trying to remove all the
//pages except the xml source page.
this.setActivePage(getPageCount() - 1);
this.removePage(0);
}
if (rootStructureNode != null) {
this.pageRootStructureNodeModel = new SimplePropertyValueModel<JpaStructureNode>(rootStructureNode);
this.addSpecificPages();
}
}
protected JpaStructureNode getRootStructureNode() {
return this.rootStructureNodeModel.getValue();
}
//*should* be only 1 root structure node for the jpa file (this is true for persistence.xml and orm.xml files)
protected PropertyValueModel<JpaStructureNode> buildRootStructureNodeModel() {
return new CollectionPropertyValueModelAdapter<JpaStructureNode, JpaStructureNode>(this.buildRootStructureNodesCollectionModel()) {
@Override
protected JpaStructureNode buildValue() {
return this.collectionModel.size() > 0 ? this.collectionModel.iterator().next() : null;
}
};
}
protected CollectionValueModel<JpaStructureNode> buildRootStructureNodesCollectionModel() {
return new CollectionAspectAdapter<JpaFile, JpaStructureNode>(this.buildJpaFileModel(), JpaFile.ROOT_STRUCTURE_NODES_COLLECTION) {
@Override
protected Iterable<JpaStructureNode> getIterable() {
return this.subject.getRootStructureNodes();
}
@Override
protected int size_() {
return this.subject.getRootStructureNodesSize();
}
};
}
/* CU private */ class SetPageRootStructureNodeRunnable
implements Runnable
{
private final JpaStructureNode jpaStructureNode;
SetPageRootStructureNodeRunnable(JpaStructureNode jpaStructureNode) {
super();
this.jpaStructureNode = jpaStructureNode;
}
public void run() {
JpaXmlEditor.this.setPageRootStructureNode_(this.jpaStructureNode);
}
@Override
public String toString() {
return ObjectTools.toString(this, this.jpaStructureNode);
}
}
@Override
public void dispose() {
this.editorInputModel.setValue(null);
this.localResourceManager.dispose();
this.rootStructureNodeModel.removePropertyChangeListener(PropertyValueModel.VALUE, this.rootStructureNodeListener);
super.dispose();
}
// ********** misc **********
@Override
public IFileEditorInput getEditorInput() {
return (IFileEditorInput) super.getEditorInput();
}
public WidgetFactory getWidgetFactory() {
return this.widgetFactory;
}
private void execute(Runnable runnable) {
SWTUtil.execute(this.getSite().getShell().getDisplay(), runnable);
}
@Override
public String toString() {
return ObjectTools.toString(this, this.editorInputModel.getValue().getFile());
}
// ********** form page **********
/**
* This extension over <code>FormPage</code> simply completes the layout by
* using the <code>JpaEditorPageDefinition</code>
*
* @see JpaEditorPageDefinition#buildEditorPageContent(IManagedForm, WidgetFactory, PropertyValueModel)
*/
class Page
extends FormPage
{
/**
* The editor page definition, find the page's text, image, help ID
* and build the content.
*/
private final JpaEditorPageDefinition editorPageDefinition;
/**
* The FormPage's image descriptor, stored so that we can dispose of it.
*/
private ImageDescriptor imageDescriptor;
Page(JpaEditorPageDefinition editorPageDefinition) {
super(JpaXmlEditor.this,
editorPageDefinition.getClass().getName(),
editorPageDefinition.getPageText());
this.editorPageDefinition = editorPageDefinition;
}
@Override
protected void createFormContent(IManagedForm managedForm) {
ScrolledForm form = managedForm.getForm();
managedForm.getToolkit().decorateFormHeading(form.getForm());
// Update the text and image
updateForm(form);
// Update the layout
updateBody(managedForm);
// This will finish the initialization of the buttons
updateHelpButton();
form.updateToolBar();
}
/**
* Updates the text and image of the form. The image can be null.
*
*/
private void updateForm(ScrolledForm form) {
form.setText(this.editorPageDefinition.getPageText());
this.imageDescriptor = this.editorPageDefinition.getPageImageDescriptor();
if (this.imageDescriptor != null) {
form.setImage(JpaXmlEditor.this.localResourceManager.createImage(this.imageDescriptor));
}
}
/**
* Adds the page's control to this page.
*/
private void updateBody(IManagedForm form) {
Composite body = form.getForm().getBody();
body.setLayout(new GridLayout(1, true));
this.editorPageDefinition.buildEditorPageContent(form, getWidgetFactory(), JpaXmlEditor.this.pageRootStructureNodeModel);
//calling this because it makes the scroll bar appear on the editor tabs when the content
//is larger than the editor tab area. Not sure how else to make this happen
form.reflow(true);
}
/**
* Adds a help button to the page's toolbar if a help ID exists.
*/
private void updateHelpButton() {
String helpID = this.editorPageDefinition.getHelpID();
if (helpID != null) {
Action helpAction = new HelpAction(helpID);
ScrolledForm form = getManagedForm().getForm();
IToolBarManager manager = form.getToolBarManager();
manager.add(helpAction);
}
}
@Override
public void dispose() {
JpaXmlEditor.this.localResourceManager.destroyImage(this.imageDescriptor);
super.dispose();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
StringBuilderTools.appendHashCodeToString(sb, this);
sb.append('(');
sb.append(this.editorPageDefinition.getPageText());
sb.append(')');
return sb.toString();
}
private class HelpAction
extends Action
{
final String helpID;
HelpAction(String helpID) {
super(JptUiMessages.JpaXmlEditor_page_help);
this.helpID = helpID;
}
@Override
public ImageDescriptor getImageDescriptor() {
return PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_LCL_LINKTO_HELP);
}
@Override
public void run() {
BusyIndicator.showWhile(getManagedForm().getForm().getDisplay(), new Runnable() {
public void run() {
PlatformUI.getWorkbench().getHelpSystem().displayHelp(HelpAction.this.helpID);
}
});
}
}
}
}