| //------------------------------------------------------------------------------ |
| // 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.diagramming.base.providers; |
| |
| import java.io.ByteArrayInputStream; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.filebuffers.manipulation.ContainerCreator; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.IStorage; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.transaction.TransactionalEditingDomain; |
| import org.eclipse.emf.workspace.AbstractEMFOperation; |
| import org.eclipse.emf.workspace.util.WorkspaceSynchronizer; |
| import org.eclipse.epf.diagram.core.part.DiagramFileEditorInputProxy; |
| import org.eclipse.epf.diagram.core.part.IDiagramFileEditorInputProxy; |
| import org.eclipse.epf.diagramming.base.DiagrammingResources; |
| import org.eclipse.epf.diagramming.base.persistence.DiagramPersister; |
| import org.eclipse.epf.diagramming.base.persistence.DiagramService; |
| import org.eclipse.epf.diagramming.base.persistence.ISaveEventDispatcher; |
| import org.eclipse.epf.diagramming.base.persistence.ISaveInfo; |
| import org.eclipse.epf.diagramming.base.persistence.SaveEventDispatcher; |
| import org.eclipse.epf.diagramming.base.persistence.SaveInfo; |
| import org.eclipse.epf.diagramming.base.util.UmaUmlUtil; |
| import org.eclipse.epf.diagramming.part.EPFDiagramEditorPlugin; |
| 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.emf.core.resources.GMFResource; |
| import org.eclipse.gmf.runtime.notation.Diagram; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IStorageEditorInput; |
| import org.eclipse.uml2.uml.Activity; |
| import org.eclipse.uml2.uml.NamedElement; |
| |
| /** |
| * Specialized document provider for handle changes and manipulate document associated |
| * and synchronize the other opened editor's document and do merge any conflict synchronize. |
| * Also handles the timestamp synchronization if the multiple editors are sharing same resource. |
| * @author Shashidhar Kannoori |
| */ |
| public class DiagramEditorDocumentProvider extends FileDiagramDocumentProvider { |
| |
| /** |
| * |
| */ |
| public DiagramEditorDocumentProvider() { |
| // TODO Auto-generated constructor stub |
| } |
| |
| /** |
| * (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.FileDiagramDocumentProvider#setDocumentContent(org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.IDocument, org.eclipse.ui.IEditorInput) |
| * |
| * @modified |
| */ |
| protected boolean setDocumentContent(IDocument document, |
| IEditorInput editorInput) throws CoreException { |
| if (editorInput instanceof DiagramFileEditorInputProxy) { |
| DiagramFileEditorInputProxy diagramElement = (DiagramFileEditorInputProxy) editorInput; |
| |
| ((IDiagramDocument) document).setEditingDomain(diagramElement |
| .getEditingDomain()); |
| if (editorInput instanceof IStorageEditorInput) { |
| IStorage storage = ((IStorageEditorInput) editorInput) |
| .getStorage(); |
| setDocumentContentFromStorage(document, storage, editorInput); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| protected void setupDocument(Object element, IDocument document) { |
| super.setupDocument(element, document); |
| } |
| |
| /** |
| * (non-Javadoc) |
| * @custom |
| */ |
| protected void setDocumentContentFromStorage(IDocument document, |
| IStorage storage, IEditorInput editorInput) 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, |
| "1", |
| 1, |
| DiagrammingResources.FileDocumentProvider_handleElementContentChanged, |
| null)); |
| } |
| } |
| } |
| IDiagramDocument diagramDocument = (IDiagramDocument) document; |
| TransactionalEditingDomain domain = diagramDocument.getEditingDomain(); |
| |
| diagram = DiagramPersister.load(domain, storage, true, |
| getProgressMonitor(), editorInput); |
| document.setContent(diagram); |
| //super.setDocumentContentFromStorage(document, storage); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.FileDiagramDocumentProvider#handleElementContentChanged(org.eclipse.ui.IFileEditorInput) |
| * @custom |
| */ |
| protected void handleElementContentChanged(IFileEditorInput fileEditorInput) { |
| |
| FileInfo info= (FileInfo) getElementInfo(fileEditorInput); |
| if (info == null) |
| return; |
| |
| IDocument document= createEmptyDocument(); |
| IStatus status= null; |
| |
| try { |
| |
| try { |
| refreshFile(fileEditorInput.getFile()); |
| } catch (CoreException x) { |
| handleCoreException(x, "File Changed outside"); |
| } |
| |
| setDocumentContent(document, fileEditorInput); |
| |
| } 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(fileEditorInput); |
| |
| removeUnchangedElementListeners(fileEditorInput, info); |
| |
| info.fDocument.removeDocumentListener(info); |
| info.fDocument.setContent(newContent); |
| info.fCanBeSaved= false; |
| info.fModificationStamp= computeModificationStamp(fileEditorInput.getFile()); |
| info.fStatus= status; |
| |
| addUnchangedElementListeners(fileEditorInput, info); |
| |
| fireElementContentReplaced(fileEditorInput); |
| |
| } |
| } |
| protected void handleElementMoved(IFileEditorInput fileEditorInput, IPath path) { |
| super.handleElementMoved(fileEditorInput, path); |
| } |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.FileDiagramDocumentProvider#createInputWithEditingDomain(org.eclipse.ui.IEditorInput, org.eclipse.emf.transaction.TransactionalEditingDomain) |
| * @custom |
| */ |
| public IEditorInput createInputWithEditingDomain(IEditorInput editorInput, TransactionalEditingDomain domain) { |
| if(editorInput instanceof IDiagramFileEditorInputProxy){ |
| ((IDiagramFileEditorInputProxy)editorInput).setTransactionalEditingDomain(domain); |
| return editorInput; |
| }else{ |
| return super.createInputWithEditingDomain(editorInput, domain); |
| } |
| } |
| /** |
| * @author Shashidhar Kannoori |
| * @custom |
| */ |
| public class DiagramFileInfoSync extends DiagramFileInfo{ |
| |
| ISaveEventDispatcher dispatcher; |
| IFileEditorInput input; |
| public boolean refreshedByShared = false; |
| public DiagramFileInfoSync(IDocument document, FileSynchronizer fileSynchronizer, DiagramModificationListener listener, |
| IFileEditorInput input) { |
| super(document, fileSynchronizer, listener); |
| final IDocument inUseDocument = document; |
| final DiagramFileEditorInputProxy inputProxy =(DiagramFileEditorInputProxy)input; |
| if(dispatcher == null){ |
| dispatcher = new SaveEventDispatcher(){ |
| public void updateTimeStamp(ISaveInfo info) { |
| fModificationStamp = info.getTimeStamp(); |
| // Object object = info.getSource(); |
| // |
| // if(object instanceof ElementInfo){ |
| // |
| // if(inUseDocument.equals(((ElementInfo)object).fDocument)){ |
| // return; |
| // } |
| // } |
| // MergeOperation operation = new MergeOperation(inputProxy.getEditingDomain(), |
| // "Document Merge Operation", (ElementInfo)object, inUseDocument); |
| // try{ |
| // operation.execute(new NullProgressMonitor(), null ); |
| // }catch(ExecutionException exception){ |
| // exception.printStackTrace(); |
| // } |
| } |
| }; |
| if(dispatcher != null){ |
| DiagramService.eInstance().addDispatchers(dispatcher); |
| } |
| } |
| this.input = input; |
| |
| } |
| public void updateTimeStamp(){ |
| long timestamp = getModificationStamp(input); |
| SaveInfo info = DiagramService.eInstance().getSaveInfo(this, timestamp); |
| refreshedByShared = true; |
| DiagramService.eInstance().dispatch(info); |
| refreshedByShared = false; |
| } |
| public void updateTimeStamp(IFile file){ |
| long timestamp = computeModificationStamp(file); |
| SaveInfo info = DiagramService.eInstance().getSaveInfo(this, timestamp); |
| DiagramService.eInstance().dispatch(info); |
| } |
| |
| |
| public void disposeDispatcher(){ |
| if(dispatcher !=null) |
| DiagramService.eInstance().removeDispatchers(dispatcher); |
| } |
| } |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.FileDiagramDocumentProvider#doSaveDocument(org.eclipse.core.runtime.IProgressMonitor, java.lang.Object, org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.IDocument, boolean) |
| * @custom |
| */ |
| protected void doSaveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite) throws CoreException { |
| if (element instanceof IFileEditorInput) { |
| |
| IFileEditorInput input= (IFileEditorInput) element; |
| FileInfo info= (FileInfo) getElementInfo(element); |
| IFile file= input.getFile(); |
| |
| if (file.exists()) { |
| |
| if (info != null && !overwrite) |
| checkSynchronizationState(info.fModificationStamp, file); |
| |
| // inform about the upcoming content change |
| fireElementStateChanging(element); |
| try { |
| System.out.println("Time timestamp before saving: " + computeModificationStamp(file)); |
| saveDocumentToFile(document, file, overwrite, monitor); |
| // Dispatch the save event. |
| if(info instanceof DiagramFileInfoSync){ |
| ((DiagramFileInfoSync)info).updateTimeStamp(); |
| } |
| System.out.println("Time timestamp after saving: " + computeModificationStamp(file)); |
| |
| } catch (CoreException x) { |
| // inform about failure |
| fireElementStateChangeFailed(element); |
| throw x; |
| } catch (RuntimeException x) { |
| // inform about failure |
| fireElementStateChangeFailed(element); |
| throw x; |
| } |
| |
| // If here, the editor state will be flipped to "not dirty". |
| // Thus, the state changing flag will be reset. |
| |
| if (info != null) { |
| info.fModificationStamp= computeModificationStamp(file); |
| } |
| |
| } else { |
| try { |
| monitor.beginTask(DiagrammingResources.FileDocumentProvider_task_saving, 3000); |
| ContainerCreator creator = new ContainerCreator(file.getWorkspace(), file.getParent().getFullPath()); |
| creator.createContainer(new SubProgressMonitor(monitor, 1000)); |
| file.create(new ByteArrayInputStream("".getBytes()), false, new SubProgressMonitor(monitor, 1000)); //$NON-NLS-1$ |
| saveDocumentToFile(document, file, overwrite, new SubProgressMonitor(monitor, 1000)); |
| } |
| finally { |
| monitor.done(); |
| } |
| } |
| } else { |
| super.doSaveDocument(monitor, element, document, overwrite); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.resources.editor.ide.document.FileDiagramDocumentProvider#disposeElementInfo(java.lang.Object, org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.AbstractDocumentProvider.ElementInfo) |
| * @custom |
| */ |
| protected void disposeElementInfo(Object element, ElementInfo info) { |
| if(info instanceof DiagramFileInfoSync){ |
| ((DiagramFileInfoSync)info).disposeDispatcher(); |
| } |
| super.disposeElementInfo(element, info); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.editor.AbstractDocumentProvider#createElementInfo(java.lang.Object) |
| */ |
| protected ElementInfo createElementInfo(Object element) throws CoreException { |
| if (element instanceof IFileEditorInput) { |
| |
| IFileEditorInput input= (IFileEditorInput) element; |
| |
| try { |
| refreshFile(input.getFile()); |
| } catch (CoreException x) { |
| handleCoreException(x, DiagrammingResources.FileDocumentProvider_createElementInfo); |
| } |
| |
| IDocument d= null; |
| IStatus s= null; |
| |
| try { |
| d= createDocument(element); |
| } catch (CoreException x) { |
| handleCoreException(x, DiagrammingResources.FileDocumentProvider_createElementInfo); |
| s= x.getStatus(); |
| d= createEmptyDocument(); |
| } |
| |
| DiagramFileSynchronizer f= new DiagramFileSynchronizer(input); |
| f.install(); |
| |
| FileInfo info= createFileInfo(d, f, input); |
| info.fModificationStamp= computeModificationStamp(input.getFile()); |
| info.fStatus= s; |
| |
| return info; |
| } |
| |
| return super.createElementInfo(element); |
| } |
| |
| /** |
| * Does merge operation of the resource if resource contents are saved by shared |
| * IDocument. |
| * @author Shashidhar Kannoori |
| */ |
| public class MergeOperation extends AbstractEMFOperation{ |
| |
| private ElementInfo elementInfo; |
| private IDocument toMergeDocument; |
| |
| public MergeOperation(TransactionalEditingDomain domain, String label, |
| ElementInfo elementInfo, IDocument toMergeDocument) { |
| super(domain, label); |
| this.elementInfo = elementInfo; |
| this.toMergeDocument = toMergeDocument; |
| } |
| |
| protected IStatus doExecute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { |
| try{ |
| if(elementInfo instanceof ElementInfo){ |
| IDocument savedDocument = ((ElementInfo)elementInfo).fDocument; |
| Diagram saveddiagram = (Diagram)savedDocument.getContent(); |
| if(saveddiagram != null){ |
| Diagram inUseDiagram = (Diagram)toMergeDocument.getContent(); |
| GMFResource gmfResource = (GMFResource)inUseDiagram.eResource(); |
| EList list = gmfResource.getContents(); |
| |
| String guid = UmaUmlUtil.getUmaGuidFromUmlElement((NamedElement)saveddiagram.getElement()); |
| Diagram replacingDiagram = null; |
| List newList = new ArrayList(); |
| for (Iterator iter = list.iterator(); iter.hasNext();) { |
| EObject element = (EObject) iter.next(); |
| if(element instanceof Activity){ |
| String currentguid = UmaUmlUtil.getUmaGuidFromUmlElement((NamedElement)element); |
| if(currentguid.equals(guid)){ |
| newList.add(saveddiagram.getElement()); |
| }else{ |
| newList.add(element); |
| } |
| } |
| if(element instanceof Diagram){ |
| Diagram diagram = (Diagram)element; |
| EObject object = ((Diagram)element).getElement(); |
| String rGuid = UmaUmlUtil.getUmaGuidFromUmlElement((NamedElement)object); |
| if(guid.equals(rGuid)){ |
| replacingDiagram = diagram; |
| newList.add(saveddiagram); |
| }else{ |
| newList.add(element); |
| } |
| } |
| } |
| |
| if(replacingDiagram == null){ |
| newList.add(saveddiagram.getElement()); |
| newList.add(saveddiagram); |
| } |
| //Transaction tx = create |
| // TODO handle better way to merge the elements. |
| if(!newList.isEmpty()){ |
| boolean notification = gmfResource.eDeliver(); |
| gmfResource.eSetDeliver(false); |
| gmfResource.getContents().clear(); |
| gmfResource.getContents().addAll(newList); |
| gmfResource.eSetDeliver(notification); |
| //list.addAll(newList); |
| } |
| } |
| } |
| }catch(Exception e){ |
| System.out.println("Warning: Document merge failed due to "+ e.getMessage()); |
| return Status.CANCEL_STATUS; |
| } |
| return Status.OK_STATUS; |
| } |
| } |
| |
| |
| /** |
| * Handles the saving of the diagram to a file |
| * |
| * @param domain |
| * the TransactionalEditingDomain we are saving in |
| * @param file |
| * the IFile to save to |
| * @param diagram |
| * Diagram that will be saved |
| * @param options |
| * save options or null |
| * @param monitor |
| * IProgressMonitor |
| * @throws CoreException |
| */ |
| protected void doSave(TransactionalEditingDomain domain, IFile file, |
| Diagram diagram, Map options, IProgressMonitor monitor) |
| throws CoreException { |
| if (options == null) { |
| DiagramPersister.save(domain, file, diagram, DiagramPersister |
| .hasUnrecognizedData(diagram.eResource()), monitor); |
| } else { |
| DiagramPersister.save(domain, file, diagram, monitor, options); |
| } |
| } |
| |
| /** |
| * Synchronizes the document with external resource changes. |
| */ |
| protected class DiagramFileSynchronizer extends FileSynchronizer { |
| |
| /** |
| * Creates a new file synchronizer. Is not yet installed on a resource. |
| * |
| * @param fileEditorInput the editor input to be synchronized |
| */ |
| public DiagramFileSynchronizer(IFileEditorInput fileEditorInput) { |
| super(fileEditorInput); |
| } |
| /* |
| * @see IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) |
| */ |
| public boolean visit(IResourceDelta delta) throws CoreException { |
| if (delta == null) |
| return false; |
| |
| delta= delta.findMember(getFile().getFullPath()); |
| |
| if (delta == null) |
| return false; |
| |
| Runnable runnable= null; |
| |
| switch (delta.getKind()) { |
| case IResourceDelta.CHANGED: |
| DiagramFileInfoSync info= (DiagramFileInfoSync) getElementInfo(fFileEditorInput); |
| if (info == null || info.fCanBeSaved) |
| break; |
| |
| boolean isSynchronized= computeModificationStamp(getFile()) == info.fModificationStamp; |
| if (((IResourceDelta.ENCODING & delta.getFlags()) != 0 && isSynchronized) || ((IResourceDelta.CONTENT & delta.getFlags()) != 0 && !isSynchronized) |
| || info.refreshedByShared) { |
| runnable = new SafeChange(fFileEditorInput) { |
| protected void execute(IFileEditorInput input) throws Exception { |
| //handleElementContentChanged(input); |
| } |
| }; |
| } |
| break; |
| |
| case IResourceDelta.REMOVED: |
| if ((IResourceDelta.MOVED_TO & delta.getFlags()) != 0) { |
| final IPath path= delta.getMovedToPath(); |
| runnable= new SafeChange(fFileEditorInput) { |
| protected void execute(IFileEditorInput input) throws Exception { |
| handleElementMoved(input, path); |
| } |
| }; |
| } else { |
| info= (DiagramFileInfoSync) getElementInfo(fFileEditorInput); |
| if (info != null && !info.fCanBeSaved) { |
| runnable= new SafeChange(fFileEditorInput) { |
| protected void execute(IFileEditorInput input) throws Exception { |
| handleElementDeleted(input); |
| } |
| }; |
| } |
| } |
| break; |
| } |
| |
| if (runnable != null) |
| update(runnable); |
| |
| return false; |
| } |
| } |
| |
| public void unsetCanSaveDocument(Object element) { |
| if (element != null) { |
| ElementInfo info = (ElementInfo) getElementInfo(element); |
| if (info != null) { |
| removeUnchangedElementListeners(element, info); |
| info.fCanBeSaved = false; |
| addUnchangedElementListeners(element, info); |
| fireElementDirtyStateChanged(element, info.fCanBeSaved); |
| } |
| } |
| } |
| } |