Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvhemery2011-09-29 08:50:01 +0000
committervhemery2011-09-29 08:50:01 +0000
commit0f55f1d0de2836c51fffd93f371da6ceaaa93771 (patch)
tree449defcc8600be9ff64f9907c5dfe75c876a985c
parentdcd722aab65adf9c0092c7593efc7e4837220f8f (diff)
downloadorg.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.
-rw-r--r--plugins/uml/org.eclipse.papyrus.diagram.common/messages.properties14
-rw-r--r--plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/Messages.java30
-rw-r--r--plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/PartActivationListener.java353
-rw-r--r--plugins/uml/org.eclipse.papyrus.diagram.common/src/org/eclipse/papyrus/diagram/common/resourceupdate/ResourceUpdateService.java17
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());
}
}
}

Back to the top