blob: a1bdd56fd5276f43e0cad17a167c77378dbbb2dc [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2005, 2007 IBM Corporation and others.
// 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:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Copyright (c) 2005, 2007 IBM Corporation and others.
// 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:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.diagram.core.providers;
import java.util.Iterator;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
import org.eclipse.epf.common.CommonPlugin;
import org.eclipse.epf.common.ui.util.MsgBox;
import org.eclipse.epf.diagram.core.DiagramCorePlugin;
import org.eclipse.epf.diagram.core.part.DiagramEditorInputProxy;
import org.eclipse.epf.diagram.core.part.IDiagramFileEditorInputProxy;
import org.eclipse.epf.diagram.core.part.util.DiagramEditorUtil;
import org.eclipse.epf.diagram.core.services.DiagramManager;
import org.eclipse.epf.diagram.core.services.FileSynchronizer;
import org.eclipse.epf.diagram.model.util.TxUtil;
import org.eclipse.epf.services.Services;
import org.eclipse.epf.services.ILibraryPersister.FailSafeMethodLibraryPersister;
import org.eclipse.epf.uma.Activity;
import org.eclipse.gmf.runtime.common.core.util.Log;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.DiagramDocument;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.DiagramModificationListener;
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.ide.document.FileDiagramDocumentProvider;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.StorageDiagramDocumentProvider;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.WorkspaceOperationRunner;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.internal.EditorIDEPlugin;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.internal.l10n.EditorMessages;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.internal.EditorPlugin;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.internal.EditorStatusCodes;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IStorageEditorInput;
/**
* @author Phong Nguyen Le
*
* @since 1.2
*/
public class SharedResourceDiagramDocumentProvider extends StorageDiagramDocumentProvider
{
private static class FileDiagramDocumentProviderEx extends FileDiagramDocumentProvider {
@Override
protected ISchedulingRule getSaveRule(Object element) {
if (element instanceof IFileEditorInput) {
IFileEditorInput input= (IFileEditorInput) element;
return computeSaveSchedulingRule(input.getFile());
}
return null;
}
/**
* Computes the scheduling rule needed to create or modify a resource. If
* the resource exists, its modify rule is returned. If it does not, the
* resource hierarchy is iterated towards the workspace root to find the
* first parent of <code>toCreateOrModify</code> that exists. Then the
* 'create' rule for the last non-existing resource is returned.
*
* @param toCreateOrModify the resource to create or modify
* @return the minimal scheduling rule needed to modify or create a resource
*/
private ISchedulingRule computeSaveSchedulingRule(IResource toCreateOrModify) {
if (toCreateOrModify.exists() && toCreateOrModify.isSynchronized(IResource.DEPTH_ZERO))
return fResourceRuleFactory.refreshRule(toCreateOrModify);
IResource parent= toCreateOrModify;
do {
/*
* XXX This is a workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=67601
* IResourceRuleFactory.createRule should iterate the hierarchy itself.
*/
toCreateOrModify= parent;
parent= toCreateOrModify.getParent();
} while (parent != null && !parent.exists() && !parent.isSynchronized(IResource.DEPTH_ZERO));
return fResourceRuleFactory.createRule(toCreateOrModify);
}
@Override
protected IRunnableContext getOperationRunner(IProgressMonitor monitor) {
return super.getOperationRunner(monitor);
}
}
private static final FileDiagramDocumentProviderEx fileDiagramDocumentProvider = new FileDiagramDocumentProviderEx();
//a StorageInfo with a DiagramModificationListener
private class DiagramStorageInfo extends StorageInfo {
DiagramModificationListener fListener;
public DiagramStorageInfo(IDocument document, DiagramModificationListener listener) {
super(document);
fListener = listener;
}
}
private WorkspaceOperationRunner fOperationRunner;
private DiagramManager fDiagramMgr;
private boolean locked = false;
public SharedResourceDiagramDocumentProvider(DiagramManager diagramMgr) {
super();
fDiagramMgr = diagramMgr;
}
@Override
protected boolean setDocumentContent(IDocument document, IEditorInput editorInput) throws CoreException {
if(document instanceof IDiagramDocument) {
((IDiagramDocument)document).setEditingDomain(fDiagramMgr.getEditingDomain());
}
return super.setDocumentContent(document, editorInput);
}
/*
* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.StorageDiagramDocumentProvider#setDocumentContentFromStorage(org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.IDocument, org.eclipse.core.resources.IStorage)
*/
protected void setDocumentContentFromStorage(IDocument document, IStorage storage) throws CoreException {
Diagram diagram = (Diagram)document.getContent();
if(diagram != null) {
Resource resource = diagram.eResource();
IFile resourceFile = WorkspaceSynchronizer.getFile(resource);
// unload if the resourceFile and storage is same.
// if not same throw exception.
if(resourceFile != null) {
if(resourceFile.equals(storage)) {
document.setContent(null);
} else {
throw new CoreException(new Status(IStatus.ERROR, EditorIDEPlugin.getPluginId(), EditorStatusCodes.ERROR, EditorMessages.FileDocumentProvider_handleElementContentChanged, null));
}
}
}
document.setContent(((org.eclipse.epf.diagram.core.resources.IDiagramStorage)storage).getDiagram());
}
/*
* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.StorageDiagramDocumentProvider#disposeElementInfo(java.lang.Object, org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.AbstractDocumentProvider.ElementInfo)
*/
@Override
protected void disposeElementInfo(Object element, ElementInfo info) {
((DiagramStorageInfo)info).fListener.stopListening();
}
@Override
protected void doSaveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite) throws CoreException {
final Diagram diagram = (Diagram) document.getContent();
//TODO: handle case where user did not refresh diagram after diagram's resource has been reloaded
// diagram does not have a resource in this case
// if overwrite allowed, this diagram should replace the diagram in the reloaded resource.
//
Resource resource = diagram.eResource();
if (resource != null) {
if (!overwrite) {
DiagramManager.checkSynchronizationState(resource);
}
IStatus status = Services.getAccessController().checkModify(
new Resource[] { resource }, MsgBox.getDefaultShell());
if (!status.isOK()) {
throw new CoreException(status);
}
}
// inform about the upcoming content change
fireElementStateChanging(element);
if(diagram.getElement() instanceof org.eclipse.epf.diagram.model.Diagram) {
try {
TxUtil.runInTransaction(diagram, new Runnable() {
public void run() {
diagram.persistChildren();
for (Object child : diagram.getChildren()) {
((View) child).persistChildren();
}
}
});
} catch (ExecutionException e) {
DiagramCorePlugin.getDefault().getLogger().logError(e);
}
}
FailSafeMethodLibraryPersister persister = Services.getLibraryPersister(Services.XMI_PERSISTENCE_TYPE).getFailSafePersister();
try {
// resource.save(Collections.EMPTY_MAP);
persister.save(resource);
persister.commit();
} catch (Exception e) {
CommonPlugin.getDefault().getLogger().logError(e);
persister.rollback();
// inform about failure
fireElementStateChangeFailed(element);
throw new CoreException(new Status(IStatus.ERROR, DiagramCorePlugin.PLUGIN_ID
, EditorStatusCodes.RESOURCE_FAILURE, e
.getLocalizedMessage(), null));
}
try {
fDiagramMgr.removeDiagramBackup(getActivity((IEditorInput) element), diagram.getType());
if(DiagramManager.ADD_kind.equals(diagram.getType())
|| DiagramManager.WPD_kind.equals(diagram.getType())) {
org.eclipse.epf.diagram.model.Diagram d = (org.eclipse.epf.diagram.model.Diagram) diagram.getElement();
d.setNew(false);
}
} catch (RuntimeException x) {
// inform about failure
fireElementStateChangeFailed(element);
throw x;
}
if(monitor != null) {
monitor.done();
}
logResourceErrorsAndWarnings(resource);
}
private static void logResourceErrorsAndWarnings(Resource resource) {
if (resource != null) {
for (Iterator iter = resource.getErrors().iterator(); iter.hasNext();) {
Resource.Diagnostic diagnostic = (Resource.Diagnostic) iter.next();
Log.error(EditorPlugin.getInstance(), EditorStatusCodes.ERROR, diagnostic.getMessage());
}
for (Iterator iter = resource.getWarnings().iterator(); iter.hasNext();) {
Resource.Diagnostic diagnostic = (Resource.Diagnostic) iter.next();
Log.warning(EditorPlugin.getInstance(), EditorStatusCodes.WARNING, diagnostic.getMessage());
}
}
}
@Override
public long getModificationStamp(Object element) {
if (element instanceof IFileEditorInput) {
IFileEditorInput input= (IFileEditorInput) element;
return FileSynchronizer.computeModificationStamp(input.getFile());
}
return super.getModificationStamp(element);
}
public void setContent(IEditorInput input) {
ElementInfo info = getElementInfo(input);
if (info == null)
return;
IDocument document = createEmptyDocument();
IStatus status = null;
try {
setDocumentContent(document, input);
} catch (CoreException x) {
status = x.getStatus();
}
Object newContent= document.getContent();
// set the new content and fire content related events
fireElementContentAboutToBeReplaced(input);
removeUnchangedElementListeners(input, info);
info.fDocument.removeDocumentListener(info);
info.fDocument.setContent(newContent);
info.fCanBeSaved = false;
info.fStatus = status;
addUnchangedElementListeners(input, info);
fireElementContentReplaced(input);
}
/**
* Updates the element info to a change of the file content and sends out
* appropriate notifications.
*
* @param input the input of a document editor
*/
public void handleElementContentChanged(IEditorInput input) {
ElementInfo info = getElementInfo(input);
if (info == null)
return;
IDocument document= createEmptyDocument();
IStatus status= null;
try {
setDocumentContent(document, input);
} catch (CoreException x) {
status= x.getStatus();
}
Object newContent= document.getContent();
if ( !newContent.equals(info.fDocument.getContent())) {
// set the new content and fire content related events
fireElementContentAboutToBeReplaced(input);
removeUnchangedElementListeners(input, info);
info.fDocument.removeDocumentListener(info);
info.fDocument.setContent(newContent);
info.fCanBeSaved= false;
info.fStatus= status;
addUnchangedElementListeners(input, info);
fireElementContentReplaced(input);
} else {
handleExistingDocumentSaved(input, info, status);
}
}
private void handleExistingDocumentSaved(IEditorInput input, ElementInfo info, IStatus status) {
removeUnchangedElementListeners(input, info);
// fires only the dirty state related event
info.fCanBeSaved= false;
info.fStatus= status;
addUnchangedElementListeners(input, info);
fireElementDirtyStateChanged(input, false);
}
public void markDocumentAsSaved(IFileEditorInput input) {
ElementInfo info = getElementInfo(input);
if (info == null)
return;
handleExistingDocumentSaved(input, info, null);
}
@Override
protected void doSynchronize(Object element, IProgressMonitor monitor) throws CoreException {
if(element instanceof IEditorInput) {
handleElementContentChanged((IEditorInput) element);
return;
}
super.doSynchronize(element, monitor);
}
@Override
public boolean isSynchronized(Object element) {
ElementInfo info = getElementInfo(element);
if(info.fDocument instanceof IDiagramDocument) {
Diagram diagram = ((IDiagramDocument)info.fDocument).getDiagram();
if(diagram != null) {
Resource resource = diagram.eResource();
// a diagram that does not have a resource is considered as out-of-synch
// because its resource must have been reloaded.
//
return resource != null && DiagramManager.isSynchronized(resource);
}
}
return super.isSynchronized(element);
}
@Override
public ElementInfo createNewElementInfo(IDocument document) {
DiagramModificationListener listener = new AccessibleDiagramModificationListener(this, (DiagramDocument)document);
DiagramStorageInfo info = new DiagramStorageInfo(document, listener);
listener.startListening();
return info;
}
/* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.editor.AbstractDocumentProvider#getOperationRunner(org.eclipse.core.runtime.IProgressMonitor)
*/
protected IRunnableContext getOperationRunner(IProgressMonitor monitor) {
return fileDiagramDocumentProvider.getOperationRunner(monitor);
}
/* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.editor.AbstractDocumentProvider#getSaveRule(java.lang.Object)
*/
protected ISchedulingRule getSaveRule(Object element) {
return fileDiagramDocumentProvider.getSaveRule(element);
}
/**
* Allows editing even the document is out of synch.
*
* @param input
*/
public void allowEditing(IEditorInput input) {
ElementInfo info = getElementInfo(input);
if(info.fDocument instanceof IDiagramDocument) {
Diagram diagram = ((IDiagramDocument)info.fDocument).getDiagram();
if(diagram != null && diagram.eResource() == null
&& input instanceof IDiagramFileEditorInputProxy) {
Activity act = getActivity(input);
if(act != null) {
fDiagramMgr.replaceTemporarily(act, diagram);
}
}
}
}
private static Activity getActivity(IEditorInput input) {
return (Activity) ((IDiagramFileEditorInputProxy)input).getDiagramEditorInput().getMethodElement();
}
public void reverseToSaved(IEditorInput input) {
ElementInfo info = getElementInfo(input);
if(info.fDocument instanceof IDiagramDocument) {
Diagram diagram = ((IDiagramDocument)info.fDocument).getDiagram();
if(diagram != null && diagram.eResource() != null
&& input instanceof DiagramEditorInputProxy) {
Activity act = getActivity(input);
if(act != null) {
fDiagramMgr.reverseToSaved(act, diagram, ((DiagramEditorInputProxy)input).getPreferenceHint());
}
}
}
}
/**
* If a plugin is locked, diagram editor and document provider
* should be locked, this is done after loading the diagram editor
* using this method.
* @param lock
*/
public void lock(boolean lock){
locked =lock;
}
@Override
protected void updateCache(IStorageEditorInput input) throws CoreException {
super.updateCache(input);
StorageInfo info= (StorageInfo) getElementInfo(input);
if (info != null) {
if(locked && info.fIsModifiable)
info.fIsModifiable= !locked;
}
}
/**
*
* returns the locked state of plugin.
* locked state is set using lock(boolean lock) method.
* @return
*/
public boolean getLockedState(){
return locked;
}
@Override
public boolean isModifiable(Object element) {
if(!DiagramEditorUtil.isModifiable(element)) {
return false;
}
return super.isModifiable(element);
}
}