//------------------------------------------------------------------------------ // 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); } }