diff options
author | vhemery | 2011-09-29 08:50:01 +0000 |
---|---|---|
committer | vhemery | 2011-09-29 08:50:01 +0000 |
commit | 0f55f1d0de2836c51fffd93f371da6ceaaa93771 (patch) | |
tree | 449defcc8600be9ff64f9907c5dfe75c876a985c | |
parent | dcd722aab65adf9c0092c7593efc7e4837220f8f (diff) | |
download | org.eclipse.papyrus-0f55f1d0de2836c51fffd93f371da6ceaaa93771.tar.gz org.eclipse.papyrus-0f55f1d0de2836c51fffd93f371da6ceaaa93771.tar.xz org.eclipse.papyrus-0f55f1d0de2836c51fffd93f371da6ceaaa93771.zip |
NEW - bug 313988: Wish: reload model operation
https://bugs.eclipse.org/bugs/show_bug.cgi?id=313988
The automatic reload mechanism has been extended to the situation when an additional resource is modified (this includes controlled sub-models).
In such a case, only the updated model is reloaded, since we know how to perform local reloads (see bug #354220).
For user messages, we do not list resources, but models instead (excluding file extension), since with the one-file feature, unexperienced users no longer see the file extensions.
4 files changed, 360 insertions, 54 deletions
diff --git a/plugins/uml/org.eclipse.papyrus.diagram.common/messages.properties b/plugins/uml/org.eclipse.papyrus.diagram.common/messages.properties index cc9ff0033ae..cc948ac021b 100644 --- a/plugins/uml/org.eclipse.papyrus.diagram.common/messages.properties +++ b/plugins/uml/org.eclipse.papyrus.diagram.common/messages.properties @@ -153,4 +153,16 @@ SetNameDiagram=Set Name diagram ShowHideContentsAction_Message=Select the elements to show. \n Only items that can be displayed in the visible compartments are proposed. ShowHideContentsAction_Title=Select the elements to show -EditorLabelProvider_No_name=No name
\ No newline at end of file +EditorLabelProvider_No_name=No name + +########### Messages for conflicting changes in several editors ########## +PartActivationListener_ChangedMainMsg_many=The following models, that are in use by a Papyrus editor, have changed. Do you want to reopen the editor in order to update their content?%s +PartActivationListener_ChangedMainMsg_single=The model %s that is in use by a Papyrus editor has changed. Do you want to reopen the editor in order to update its content? +PartActivationListener_ChangedMainWarning=CAVEAT: the editor contains unsaved modifications that would be lost. +PartActivationListener_ChangedMsg_many=The following models, that are in use by a Papyrus editor, have changed. Do you want to reload these models in order to update their content?%s +PartActivationListener_ChangedMsg_single=The model %s that is in use by a Papyrus editor has changed. Do you want to reload this model in order to update its content? +PartActivationListener_ChangedTitle=Resource change +PartActivationListener_ChangedWarning=CAVEAT: the editor contains unsaved modifications that would be lost for these submodels : %s +PartActivationListener_RemovedMsg_many=The following models, that are in use by a Papyrus editor, have been removed. Use save/save as, if you want to keep the models.%s +PartActivationListener_RemovedMsg_single=The model %s that is in use by a Papyrus editor has been removed. Use save/save as, if you want to keep the model +PartActivationListener_RemovedTitle=Resource removal diff --git a/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/Messages.java b/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/Messages.java index cd2f3265f6f..dec1516823e 100644 --- a/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/Messages.java +++ b/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/Messages.java @@ -298,6 +298,36 @@ public class Messages extends NLS { /** label for the tooltip of the edit drawer icon */ public static String PapyrusPaletteCustomizerDialog_EditButtonTooltip; + /** message when many resources have changed (including main) */ + public static String PartActivationListener_ChangedMainMsg_many; + + /** message when main resource has changed */ + public static String PartActivationListener_ChangedMainMsg_single; + + /** message when main resource is conflicting */ + public static String PartActivationListener_ChangedMainWarning; + + /** message when many resources have changed */ + public static String PartActivationListener_ChangedMsg_many; + + /** message when single resource has changed */ + public static String PartActivationListener_ChangedMsg_single; + + /** title when resources have changed */ + public static String PartActivationListener_ChangedTitle; + + /** message when resource is conflicting */ + public static String PartActivationListener_ChangedWarning; + + /** message when many resources have been removed */ + public static String PartActivationListener_RemovedMsg_many; + + /** message when single resource has been removed */ + public static String PartActivationListener_RemovedMsg_single; + + /** title when resources have been removed */ + public static String PartActivationListener_RemovedTitle; + /** Message for dialog to edit the runtime properties */ public static String StereotypePostAction_EditRuntimePropertiesMessage; diff --git a/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/PartActivationListener.java b/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/PartActivationListener.java index 788743b0acd..196339613bd 100644 --- a/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/PartActivationListener.java +++ b/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/PartActivationListener.java @@ -9,6 +9,7 @@ * * Contributors: * Ansgar Radermacher (CEA LIST) ansgar.radermacher@cea.fr - Initial API and implementation + * Vincent Hemery (Atos) - Also take in account modifications for sub-models * *****************************************************************************/ @@ -16,10 +17,29 @@ package org.eclipse.papyrus.diagram.common.resourceupdate; import static org.eclipse.papyrus.core.Activator.log; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.emf.common.command.AbstractCommand; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.papyrus.core.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.core.resourceloading.util.LoadingUtils; import org.eclipse.papyrus.core.utils.EditorUtils; +import org.eclipse.papyrus.diagram.common.Messages; +import org.eclipse.papyrus.resource.ModelSet; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorDescriptor; @@ -39,17 +59,67 @@ import org.eclipse.ui.PlatformUI; * @author Ansgar Radermacher (CEA LIST) */ public class PartActivationListener implements IPartListener { + + /** + * This class is a simple pair storing a resource delta and whether this delta causes conflict with editor's resource + * + * @author vhemery + */ + private static class ResourceModification { + + private IResourceDelta delta; + + private boolean conflict; + + public ResourceModification(IResourceDelta resourceDelta, boolean resourceConflicts) { + delta = resourceDelta; + conflict = resourceConflicts; + } + } + + /** editor to update when activated */ + private IMultiDiagramEditor editor; + + /** paths for modified main resources */ + private List<IPath> changedMainResourcePaths; + + /** modifications performed on all edited resources */ + private Map<IPath, ResourceModification> resourceModifications; public PartActivationListener(IMultiDiagramEditor editor) { this.editor = editor; - modifiedMainResource = false; + resourceModifications = new HashMap<IPath, ResourceModification>(); + changedMainResourcePaths = new ArrayList<IPath>(); } /** - * @return true, if a resource for an underlying editor have been updated? + * Test if main model has changed + * + * @return true, when a resource for the underlying editor's main model has been updated + * @deprecated use {@link #isMainModelModified()} instead */ public boolean isModied() { - return modifiedMainResource; + return isMainModelModified(); + } + + /** + * Test if main model has changed + * + * @return true, when a resource for the underlying editor's main model has been updated + */ + public boolean isMainModelModified() { + return !changedMainResourcePaths.isEmpty(); + } + + /** + * Check if an underlying resource has changed + * + * @param resourcePathToTest + * path of resource to check changes for (including file extension) + * @return true, when this resource has been updated for this underlying editor + */ + public boolean isModified(IPath resourcePathToTest) { + return resourceModifications.containsKey(resourcePathToTest); } /** @@ -59,11 +129,42 @@ public class PartActivationListener implements IPartListener { * The path to the resource that has been changed * @param delta * additional information about the change + * @deprecated use {@link #setModificationData(IPath, IResourceDelta, boolean)} instead */ public void setModificationData(String changedResourcePath, IResourceDelta delta) { - this.changedResourcePath = changedResourcePath; - this.delta = delta; - modifiedMainResource = true; + IPath path = Path.fromPortableString(changedResourcePath); + setModificationData(path, delta, true, true); + } + + /** + * Indicates that a resource for an editor has been modified + * + * @param changedResourcePath + * The path to the resource that has been changed (including file extension) + * @param delta + * additional information about the change + * @param isMainResource + * true if resource is part of the main model + * @param resourceConflicts + * true if the resource contains modifications that should conflict with these changes + */ + public void setModificationData(IPath changedResourcePath, IResourceDelta delta, boolean isMainResource, boolean resourceConflicts) { + if(resourceModifications.containsKey(changedResourcePath)){ + //merge two modifications : 1. merge conflicts the pessimistic way + resourceModifications.get(changedResourcePath).conflict |= resourceConflicts; + //merge two modifications : 2. take latest delta + /* + * Some delta information is lost, but kind is the best one : + * If new delta kind is REMOVED, then ok : resource removed at the end + * If new delta kind is ADDED, then there should not be any delta before + * If new delta kind is CHANGED, then the action taken for CHANGED is more suitable than others + */ + resourceModifications.get(changedResourcePath).delta = delta; + } + resourceModifications.put(changedResourcePath, new ResourceModification(delta, resourceConflicts)); + if(isMainResource) { + changedMainResourcePaths.add(changedResourcePath); + } } public void partActivated(IWorkbenchPart part) { @@ -71,34 +172,127 @@ public class PartActivationListener implements IPartListener { // (e.g. model explorer or property) // of an active editor may actually be selected IMultiDiagramEditor activeEditor = EditorUtils.getMultiDiagramEditor(); - if((editor == activeEditor) && modifiedMainResource) { - - switch(delta.getKind()) { - case IResourceDelta.ADDED: - break; - case IResourceDelta.REMOVED: - // asynchronous notification to avoid that the removal of - // multiple resource files - // belonging to the editor (e.g. .uml and .notation) at the same - // time leads to multiple - // user feedback. - - MessageDialog.openInformation(new Shell(), "Resource removal", "The resource " + changedResourcePath + " that is in use by a Papyrus editor has been removed. Use save/save as, if you want to keep the model"); - break; - - case IResourceDelta.CHANGED: - // reopen the editor asynchronously to avoid that changes of - // multiple resource files - // belonging to the editor (e.g. .uml and .notation) lead to - // multiple reloads. - // de-activate until user responds to message dialog - - String message = "The resource " + changedResourcePath + " that is in use by a Papyrus editor has changed. Do you want to reopen the editor in order to update its contents?"; - if(editor.isDirty()) { - message += " CAVEAT: the editor contains unsaved modifications that would be lost."; + + if((editor == activeEditor) && !resourceModifications.isEmpty()) { + // the byte union of all kinds of delta met for main resource + int mainDeltaKinds = 0; + if(!changedMainResourcePaths.isEmpty()) { + // look up deltas for the main model + for(IPath mainPath : changedMainResourcePaths) { + ResourceModification modif = resourceModifications.get(mainPath); + mainDeltaKinds |= modif.delta.getKind(); + } + } + // handle change in any resource model (eventually main model or sub-model) + int deltaKinds = 0;// the byte union of all kinds of delta met for all resources + Set<String> addedModels = new HashSet<String>(); + Set<String> removedModels = new HashSet<String>(); + // store conflicts in addition for changed models + Map<String, Boolean> changedModels = new HashMap<String, Boolean>(); + for(Entry<IPath, ResourceModification> entry : resourceModifications.entrySet()) { + IPath path = entry.getKey(); + ResourceModification modif = entry.getValue(); + deltaKinds |= modif.delta.getKind(); + switch(modif.delta.getKind()) { + case IResourceDelta.ADDED: + addedModels.add(path.removeFileExtension().toString()); + break; + case IResourceDelta.REMOVED: + removedModels.add(path.removeFileExtension().toString()); + break; + case IResourceDelta.CHANGED: + String key = path.removeFileExtension().toString(); + // do not erase value if a conflicting resource has already been met for this model + if(!changedModels.containsKey(key) || !changedModels.get(key)) { + changedModels.put(key, modif.conflict); + } + break; + default: + break; + } + } + handleDeltaKinds(deltaKinds, addedModels, removedModels, changedModels, mainDeltaKinds); + // reinitialize modifications fields + changedMainResourcePaths.clear(); + resourceModifications.clear(); + } + } + + /** + * Handle delta kinds with appropriate message and editor action. + * + * @param deltaKinds + * the byte union of met deltas (on all resources) + * @param addedModels + * the models with added delta (taken in account if (deltaKinds & IResourceDelta.ADDED) > 0) + * @param removedModels + * the models with removed delta (taken in account if (deltaKinds & IResourceDelta.REMOVED) > 0) + * @param changedModels + * the models with changed delta (taken in account if (deltaKinds & IResourceDelta.CHANGED) > 0) and whether they conflict with current + * editor's changes + * @param mainDeltaKinds + * the byte union of met deltas (on main resources only) + */ + private void handleDeltaKinds(int deltaKinds, Set<String> addedModels, Set<String> removedModels, Map<String, Boolean> changedModels, int mainDeltaKinds) { + // use masks to check encountered delta types + if((deltaKinds & IResourceDelta.ADDED) > 0) { + // no particular treatment + } + if((deltaKinds & IResourceDelta.REMOVED) > 0) { + // asynchronous notification to avoid that the removal of + // multiple resource files + // belonging to the editor (e.g. .uml and .notation) at the same + // time leads to multiple + // user feedback. + String msg = ""; //$NON-NLS-1$ + String list = getModelsListString(removedModels); + if(removedModels.size() == 1) { + msg = String.format(Messages.PartActivationListener_RemovedMsg_single, list); + } else { + msg = String.format(Messages.PartActivationListener_RemovedMsg_many, list); + } + MessageDialog.openInformation(new Shell(), Messages.PartActivationListener_RemovedTitle, msg); + } + if((deltaKinds & IResourceDelta.CHANGED) > 0) { + // reopen the editor asynchronously to avoid that changes of + // multiple resource files + // belonging to the editor (e.g. .uml and .notation) lead to + // multiple reloads. + // de-activate until user responds to message dialog + boolean mainChanged = (mainDeltaKinds & IResourceDelta.CHANGED) > 0; + String msg = ""; //$NON-NLS-1$ + String list = getModelsListString(changedModels.keySet()); + if(changedModels.size() == 1 && mainChanged) { + msg = String.format(Messages.PartActivationListener_ChangedMainMsg_single, list); + } else if(mainChanged) { + msg = String.format(Messages.PartActivationListener_ChangedMainMsg_many, list); + } else if(changedModels.size() == 1) { + msg = String.format(Messages.PartActivationListener_ChangedMsg_single, list); + } else { + msg = String.format(Messages.PartActivationListener_ChangedMsg_many, list); + } + if(editor.isDirty() && mainChanged) { + msg += System.getProperty("line.separator"); //$NON-NLS-1$ + msg += System.getProperty("line.separator"); //$NON-NLS-1$ + msg += Messages.PartActivationListener_ChangedMainWarning; + } else if(editor.isDirty()) { + // select conflicting models only + HashSet<String> dirtyModels = new HashSet<String>(changedModels.size()); + for(Entry<String, Boolean> entry : changedModels.entrySet()) { + if(entry.getValue()) { + dirtyModels.add(entry.getKey()); + } + } + if(!dirtyModels.isEmpty()) { + String dirtyList = getModelsListString(dirtyModels); + msg += System.getProperty("line.separator"); //$NON-NLS-1$ + msg += System.getProperty("line.separator"); //$NON-NLS-1$ + msg += String.format(Messages.PartActivationListener_ChangedWarning, dirtyList); } + } - if(MessageDialog.openQuestion(new Shell(), "Resource change", message)) { + if(MessageDialog.openQuestion(new Shell(), Messages.PartActivationListener_ChangedTitle, msg)) { + if(mainChanged) { // unloading and reloading all resources of the main causes // the following problems // - since resources are removed during the modelSets unload @@ -109,7 +303,6 @@ public class PartActivationListener implements IPartListener { // - would need to reset dirty flags // => clean & simple option is to close and reopen the // editor. - Display.getCurrent().asyncExec(new Runnable() { public void run() { @@ -125,13 +318,95 @@ public class PartActivationListener implements IPartListener { } } }); + } else { + // sub models can be reloaded on their own without reloading the whole model + Object dom = editor.getAdapter(EditingDomain.class); + if(dom instanceof EditingDomain) { + Command refreshCmd = getRefreshCommand((EditingDomain)dom, changedModels.keySet()); + if(refreshCmd.canExecute()) { + ((EditingDomain)dom).getCommandStack().execute(refreshCmd); + } + } } - break; } - modifiedMainResource = false; } } + /** + * Get the command to refresh the changed models + * + * @param domain + * editing domain + * @param changedModels + * the models to refresh + * @return the command to refresh all changed models + */ + private Command getRefreshCommand(final EditingDomain domain, final Set<String> changedModels) { + Command refreshCmd = new AbstractCommand() { + + /** Model set in which to refresh models */ + private ModelSet modelSet; + + /** URIs of models to update */ + List<URI> urisToUpdate; + + public void execute() { + for(URI uri : urisToUpdate) { + LoadingUtils.unloadResourcesFromModelSet(modelSet, uri); + LoadingUtils.loadResourcesInModelSet(modelSet, uri); + } + } + + public void redo() { + execute(); + } + + @Override + public void undo() { + execute(); + } + + @Override + protected boolean prepare() { + ResourceSet set = domain.getResourceSet(); + if(set instanceof ModelSet) { + modelSet = (ModelSet)set; + urisToUpdate = new ArrayList<URI>(changedModels.size()); + for(String pathString : changedModels) { + IPath path = Path.fromPortableString(pathString); + urisToUpdate.add(URI.createPlatformResourceURI(path.toString(), true)); + } + return true; + } + return false; + } + + }; + return refreshCmd; + } + + /** + * Get formatted string with all models of the set displayed in it, separated with a line separator. + * An additional separator is added at the beginning to allow separate display of the list in case there are several elements. + * + * @param models + * set of models path + * @return formatted list string + */ + private String getModelsListString(Set<String> models) { + StringBuffer list = new StringBuffer(); + Iterator<String> it = models.iterator(); + if(models.size() == 1) { + return it.next(); + } + while(it.hasNext()) { + String model = it.next(); + list.append(System.getProperty("line.separator")); //$NON-NLS-1$ + list.append(model); + } + return list.toString(); + } + public void partDeactivated(IWorkbenchPart part) { } @@ -143,12 +418,4 @@ public class PartActivationListener implements IPartListener { public void partOpened(IWorkbenchPart part) { } - - private IMultiDiagramEditor editor; - - private String changedResourcePath; - - private IResourceDelta delta; - - private boolean modifiedMainResource; } diff --git a/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/ResourceUpdateService.java b/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/ResourceUpdateService.java index be02c369355..38d75d38274 100644 --- a/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/ResourceUpdateService.java +++ b/plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/ResourceUpdateService.java @@ -130,23 +130,20 @@ public class ResourceUpdateService implements IService, IResourceChangeListener, // model itself has changed. // mark main resource as changed. User will asked later, // when he activates the editor. - if(!saveListener.isSaveActive() && !partActivationListener.isModied()) { - partActivationListener.setModificationData(changedResourcePath, delta); + if(!saveListener.isSaveActive()) { + partActivationListener.setModificationData(changedResource.getFullPath(), delta, true, resource.isModified()); } } - // changed resource does not belong to the model, it might - // however belong to a referenced - // model. Since the referenced model is not editable (TODO: - // might change? see bug 317430), - // it can be unloaded without asking the user (it will be - // reloaded on demand) + // Changed resource does not belong to the model, it might however belong to a referenced model. + // Since the referenced model may be editable (case of controlled sub-model with write access), + // it must not be unloaded without asking the user. User will be asked when activating the editor. else if(resource.isLoaded()) { EList<EObject> contents = resource.getContents(); if((contents.size() > 0) && (contents.get(0) instanceof Profile)) { // don't touch profiles - } else { - resource.unload(); + } else if(!saveListener.isSaveActive()) { + partActivationListener.setModificationData(changedResource.getFullPath(), delta, false, resource.isModified()); } } } |