/*****************************************************************************
* Copyright (c) 2008 CEA LIST.
*
*
* 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:
* Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.common;
import java.beans.PropertyChangeSupport;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.DiagramDocument;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.IDiagramDocument;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.IDocument;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.IDocumentProvider;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.MEditingDomainElement;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.internal.l10n.EditorMessages;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorMatchingStrategy;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.part.FileEditorInput;
import org.osgi.framework.Bundle;
/**
* This class is used as an model manager for regular GMF Editor. All editor capabilities are
* neutralize, except the model load and save capabilities.
*
* @author dumoulin
*
*/
public class ModelManagerEditor {
public static final String DIAGRAM_ADDED = "DIAGRAM_ADDED"; //$NON-NLS-1$
public static final String DIAGRAM_MOVED = "DIAGRAM_MOVED"; //$NON-NLS-1$
public static final String DIAGRAM_REMOVED = "DIAGRAM_REMOVED"; //$NON-NLS-1$
/**
* The underlying document provider.
*/
protected IDocumentProvider documentProvider;
/**
* EditorInput provided when the editor is created.
*/
protected IEditorInput editorInput;
protected IEditorSite site;
/**
* Listener on diagram added/removed.
*/
private PropertyChangeSupport diagramListListener = new PropertyChangeSupport(this);
/**
* Adapter listening to diagram addition/remove events
*/
private Adapter adapter = new Adapter() {
Notifier notifier;
public Notifier getTarget() {
return notifier;
}
public boolean isAdapterForType(Object type) {
// TODO Auto-generated method stub
return false;
}
/**
* Notifies that a change to some feature has occurred.
*
* @param notification
* a description of the change.
*/
public void notifyChanged(Notification notification) {
// System.out.println("notifyChanged("+ notification +")");
// System.out.println("getEventType=" + notification.getEventType() );
// System.out.println("getFeature=" + notification.getFeature() );
// System.out.println("getNotifier=" + notification.getNotifier() );
int eventType = notification.getEventType();
if(eventType == Notification.ADD) {
Object newValue = notification.getNewValue();
if(newValue instanceof Diagram)
diagramListListener.firePropertyChange(DIAGRAM_ADDED, null, newValue);
} else if(eventType == Notification.MOVE) {
Object newValue = notification.getNewValue();
if(newValue instanceof Diagram)
diagramListListener.firePropertyChange(DIAGRAM_MOVED, null, newValue);
} else if(eventType == Notification.REMOVE) {
Object newValue = notification.getNewValue();
if(newValue == null)
Activator.log.debug(getClass().getName() + "- Warning: can't get removed object."); //$NON-NLS-1$
if(newValue instanceof Diagram)
diagramListListener.firePropertyChange(DIAGRAM_REMOVED, null, newValue);
}
}
public void setTarget(Notifier newTarget) {
notifier = newTarget;
}
};
/**
* @param hasFlyoutPalette
*/
public ModelManagerEditor(IDocumentProvider documentProvider) {
this.documentProvider = documentProvider;
}
public IDocumentProvider getDocumentProvider() {
return documentProvider;
}
/**
*
*/
public TransactionalEditingDomain getEditingDomain() {
IDocument document = getEditorInput() != null ? getDocumentProvider().getDocument(getEditorInput()) : null;
if(document instanceof IDiagramDocument) {
return ((IDiagramDocument)document).getEditingDomain();
}
return getEditorInput() instanceof MEditingDomainElement ? ((MEditingDomainElement)getEditorInput())
.getEditingDomain() : null;
}
/**
* @cdm
*/
public void setInput(IEditorInput input) {
Activator.log.debug(this + ".setInput(IEditorInput input)"); //$NON-NLS-1$
}
/**
*
* @param site
* @param input
* @throws PartInitException
*/
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
// System.out.println(this + ".init(IEditorSite site, IEditorInput input)");
this.editorInput = input;
this.site = site;
try {
getDocumentProvider().connect(input);
} catch (CoreException x) {
// code from DiagramDocumentEditor.setInput(IEditorInput)
String title = EditorMessages.Editor_error_setinput_title;
String msg = EditorMessages.Editor_error_setinput_message;
Shell shell = site.getShell();
ErrorDialog.openError(shell, title, msg, x.getStatus());
}
}
/**
* Returns diagram list listener. This listener send event whenever a diagram is added or
* removed to the eResource.
*
* @return the listener
*/
public PropertyChangeSupport getDiagramListListener() {
return diagramListListener;
}
/**
* Called when the editor should be activated. Subclass should implements this method to
* register listeners to the model.
*
*/
public void activate() {
getNotationModelEResource().eAdapters().add(adapter);
}
/**
* Called when the editor is deactivated.
*
*/
public void deactivate() {
getNotationModelEResource().eAdapters().remove(adapter);
}
private Resource getNotationModelEResource() {
// Get the default diagram document
DiagramDocument document = (DiagramDocument)getDocumentProvider().getDocument(getEditorInput());
Diagram diagram = document.getDiagram();
return diagram.eResource();
}
/**
* Get the resource for notation model.
*
* @return
*/
public Resource getNotationResource() {
return getNotationModelEResource();
}
/**
* Get the resource for the domain model.
*
* @return
*/
public Resource getDomainResource() {
// Get the default diagram document
DiagramDocument document = (DiagramDocument)getDocumentProvider().getDocument(getEditorInput());
Diagram diagram = document.getDiagram();
EObject rootObject = diagram.getElement();
return rootObject.eResource();
}
/**
* @return the editorInput
*/
public IEditorInput getEditorInput() {
return editorInput;
}
/**
* The <code>AbstractDiagramEditor</code> implementation of this <code>IEditorPart</code> method
* may be extended by subclasses.
*
* @param progressMonitor
* the progress monitor for communicating result state or <code>null</code>
*/
public void doSave(IProgressMonitor progressMonitor) {
IDocumentProvider p = getDocumentProvider();
if(p == null)
return;
if(p.isDeleted(getEditorInput())) {
if(isSaveAsAllowed()) {
/*
* 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors. Changed
* Behavior to make sure that if called inside a regular save (because of deletion
* of input element) there is a way to report back to the caller.
*/
performSaveAs(progressMonitor);
} else {
Shell shell = getSite().getShell();
String title = EditorMessages.Editor_error_save_deleted_title;
String msg = EditorMessages.Editor_error_save_deleted_message;
MessageDialog.openError(shell, title, msg);
}
} else {
updateState(getEditorInput());
validateState(getEditorInput());
performSave(false, progressMonitor);
}
}
/**
* Updates the state of the given editor input such as read-only flag.
*
* @param input
* the input to be validated
*
*/
protected void updateState(IEditorInput input) {
IDocumentProvider provider = getDocumentProvider();
try {
provider.updateStateCache(input);
// if (getDiagramEditPart() != null) {
// if(isEditable())
// getDiagramEditPart().enableEditMode();
// else
// getDiagramEditPart().disableEditMode();
// }
} catch (CoreException x) {
Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
ILog log = Platform.getLog(bundle);
log.log(x.getStatus());
}
}
/**
* Validates the state of the given editor input. The predominate intent of this method is to
* take any action probably necessary to ensure that the input can persistently be changed.
*
* @param input
* the input to be validated
*
*/
protected void validateState(IEditorInput input) {
IDocumentProvider provider = getDocumentProvider();
try {
provider.validateState(input, getSite().getShell());
} catch (CoreException x) {
IStatus status = x.getStatus();
if(status == null || status.getSeverity() != IStatus.CANCEL) {
Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
ILog log = Platform.getLog(bundle);
log.log(x.getStatus());
Shell shell = getSite().getShell();
String title = EditorMessages.Editor_error_validateEdit_title;
String msg = EditorMessages.Editor_error_validateEdit_message;
ErrorDialog.openError(shell, title, msg, x.getStatus());
}
return;
}
// if (getDiagramEditPart() != null) {
// if(isEditable())
// getDiagramEditPart().enableEditMode();
// else
// getDiagramEditPart().disableEditMode();
// }
}
/**
* @generated
*/
public boolean isSaveAsAllowed() {
return true;
}
/**
* @generated
*/
public void doSaveAs() {
performSaveAs(new NullProgressMonitor());
}
/**
* Performs the save and handles errors appropriately.
*
* @param overwrite
* indicates whether or not overwriting is allowed
* @param progressMonitor
* the monitor in which to run the operation
*
*/
protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) {
IDocumentProvider provider = getDocumentProvider();
if(provider == null)
return;
try {
provider.aboutToChange(getEditorInput());
IEditorInput input = getEditorInput();
provider.saveDocument(progressMonitor, input, getDocumentProvider().getDocument(input), overwrite);
// editorSaved();
} catch (CoreException x) {
IStatus status = x.getStatus();
if(status == null || status.getSeverity() != IStatus.CANCEL)
handleExceptionOnSave(x, progressMonitor);
} finally {
provider.changed(getEditorInput());
}
}
/**
* @generated
*/
protected void performSaveAs(IProgressMonitor progressMonitor) {
Shell shell = getSite().getShell();
IEditorInput input = getEditorInput();
SaveAsDialog dialog = new SaveAsDialog(shell);
IFile original = input instanceof IFileEditorInput ? ((IFileEditorInput)input).getFile() : null;
if(original != null) {
dialog.setOriginalFile(original);
}
dialog.create();
IDocumentProvider provider = getDocumentProvider();
if(provider == null) {
// editor has been programmatically closed while the dialog was open
return;
}
if(provider.isDeleted(input) && original != null) {
String message = NLS.bind(Messages.ModelManagerEditor_SavingDeletedFile, original.getName());
dialog.setErrorMessage(null);
dialog.setMessage(message, IMessageProvider.WARNING);
}
if(dialog.open() == Window.CANCEL) {
if(progressMonitor != null) {
progressMonitor.setCanceled(true);
}
return;
}
IPath filePath = dialog.getResult();
if(filePath == null) {
if(progressMonitor != null) {
progressMonitor.setCanceled(true);
}
return;
}
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IFile file = workspaceRoot.getFile(filePath);
final IEditorInput newInput = new FileEditorInput(file);
// Check if the editor is already open
IEditorMatchingStrategy matchingStrategy = getEditorDescriptor().getEditorMatchingStrategy();
IEditorReference[] editorRefs = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
.getEditorReferences();
for(int i = 0; i < editorRefs.length; i++) {
if(matchingStrategy.matches(editorRefs[i], newInput)) {
MessageDialog.openWarning(shell, Messages.ModelManagerEditor_SaveAsErrorTitle,
Messages.ModelManagerEditor_SaveAsErrorMessage);
return;
}
}
boolean success = false;
try {
provider.aboutToChange(newInput);
// getDocumentProvider(newInput).saveDocument(progressMonitor, newInput,
// getDocumentProvider().getDocument(getEditorInput()), true);
getDocumentProvider().saveDocument(progressMonitor, newInput,
getDocumentProvider().getDocument(getEditorInput()), true);
success = true;
} catch (CoreException x) {
IStatus status = x.getStatus();
if(status == null || status.getSeverity() != IStatus.CANCEL) {
ErrorDialog.openError(shell, Messages.ModelManagerEditor_SaveErrorTitle,
Messages.ModelManagerEditor_SaveErrorMessage, x.getStatus());
}
} finally {
provider.changed(newInput);
if(success) {
setInput(newInput);
}
}
if(progressMonitor != null) {
progressMonitor.setCanceled(!success);
}
}
/**
* Retrieves the descriptor for this editor
*
* @return the editor descriptor
*/
final protected IEditorDescriptor getEditorDescriptor() {
IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry();
IEditorDescriptor editorDesc = editorRegistry.findEditor(getSite().getId());
return editorDesc;
}
/**
* The number of re-entrances into error correction code while saving.
*
*/
private int fErrorCorrectionOnSave;
/**
* Handles the given exception. If the exception reports an out-of-sync situation, this is
* reported to the user. Otherwise, the exception is generically reported.
*
* @param exception
* the exception to handle
* @param progressMonitor
* the progress monitor
*/
protected void handleExceptionOnSave(CoreException exception, IProgressMonitor progressMonitor) {
try {
++fErrorCorrectionOnSave;
Shell shell = getSite().getShell();
boolean isSynchronized = false;
IDocumentProvider p = getDocumentProvider();
isSynchronized = p.isSynchronized(getEditorInput());
if(isNotSynchronizedException(exception) && fErrorCorrectionOnSave == 1 && !isSynchronized) {
String title = EditorMessages.Editor_error_save_outofsync_title;
String msg = EditorMessages.Editor_error_save_outofsync_message;
if(MessageDialog.openQuestion(shell, title, msg))
performSave(true, progressMonitor);
else {
/*
* 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits Set progress
* monitor to canceled in order to report back to enclosing operations.
*/
if(progressMonitor != null)
progressMonitor.setCanceled(true);
}
} else {
String title = EditorMessages.Editor_error_save_title;
String msg = EditorMessages.Editor_error_save_message;
ErrorDialog.openError(shell, title, msg, exception.getStatus());
/*
* 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits Set progress monitor
* to canceled in order to report back to enclosing operations.
*/
if(progressMonitor != null)
progressMonitor.setCanceled(true);
}
} finally {
--fErrorCorrectionOnSave;
}
}
/**
* Tells whether the given core exception is exactly the exception which is thrown for a
* non-synchronized element.
* <p>
* XXX: After 3.1 this method must be delegated to the document provider see
* </p>
*
* @param ex
* the core exception
* @return <code>true</code> iff the given core exception is exactly the exception which is
* thrown for a non-synchronized element
*
*/
private boolean isNotSynchronizedException(CoreException ex) {
if(ex == null)
return false;
IStatus status = ex.getStatus();
if(status == null || status instanceof MultiStatus)
return false;
if(status.getException() != null)
return false;
// Can't access IResourceStatus.OUT_OF_SYNC_LOCAL, using value: 274
return status.getCode() == 274;
}
/**
* @return the site
*/
public IEditorSite getSite() {
return site;
}
}