diff options
author | Christian W. Damus | 2016-05-24 21:51:43 +0000 |
---|---|---|
committer | Gerrit Code Review @ Eclipse.org | 2016-05-27 12:07:35 +0000 |
commit | c3dcb4914637e5ca63d83d16ef9a9342796b0552 (patch) | |
tree | 82a45dcab923a035c26627407fc537ce105fac31 /plugins/uml | |
parent | 5861a9b5d65ad4f5324be31c0358901e6ee7879b (diff) | |
download | org.eclipse.papyrus-c3dcb4914637e5ca63d83d16ef9a9342796b0552.tar.gz org.eclipse.papyrus-c3dcb4914637e5ca63d83d16ef9a9342796b0552.tar.xz org.eclipse.papyrus-c3dcb4914637e5ca63d83d16ef9a9342796b0552.zip |
Bug 494478: StereotypeElementListener dispatches events too early during undo/redo
https://bugs.eclipse.org/bugs/show_bug.cgi?id=494478
Restore the post-commit timing of the dispatch of custom notifications for stereotype applications generated by undo and redo of commands.
This ensures that, for example, Adapters attached directly to objects
are not notified of unapplication of a stereotype during undo before
that stereotype is actually unapplied.
The pre-commit timing of the original notifications from command
execution is preserved because:
* they do not have the problem of preceding the actual model changes
* pre-commit listeners such as applied-stereotype display edit-policies
still need to receive and react to these notifications before commit
* post-commit listeners will still receive these also, as before
Delaying the notifications to post-commit phase for undo/redo should not break any listeners' timing assumptions because only post-commit listeners could have received them, anyways: undo/redo transactions do not have the pre-commit (triggers) phase.
Change-Id: Iaeda2abfbb86d98fbdf0a93787bef25901a7e312
Diffstat (limited to 'plugins/uml')
-rw-r--r-- | plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/listeners/StereotypeElementListener.java | 89 |
1 files changed, 57 insertions, 32 deletions
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/listeners/StereotypeElementListener.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/listeners/StereotypeElementListener.java index 7505329b864..f08d8f616ae 100644 --- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/listeners/StereotypeElementListener.java +++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/listeners/StereotypeElementListener.java @@ -9,7 +9,7 @@ * Contributors: * Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Initial API and implementation * Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.fr - Bug 393532 - * Christian W. Damus - bugs 450523, 399859, 492482 + * Christian W. Damus - bugs 450523, 399859, 492482, 494478 * *****************************************************************************/ package org.eclipse.papyrus.uml.tools.listeners; @@ -38,6 +38,7 @@ import org.eclipse.emf.transaction.NotificationFilter; import org.eclipse.emf.transaction.ResourceSetChangeEvent; import org.eclipse.emf.transaction.ResourceSetListenerImpl; import org.eclipse.emf.transaction.RollbackException; +import org.eclipse.emf.transaction.Transaction; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Extension; @@ -61,6 +62,12 @@ public class StereotypeElementListener extends ResourceSetListenerImpl { private final StereotypeExtensionFinder finder; + /** Notifications to dispatch and invert at post-commit of undo/redo transactions. */ + private final List<NotificationChain> undoRedoNotifications = new ArrayList<>(); + + /** The transaction option indicating that a transaction performs undo or redo of a command. */ + private final Transaction.OptionMetadata undoRedoOption = Transaction.OptionMetadata.Registry.INSTANCE.getOptionMetadata(Transaction.OPTION_IS_UNDO_REDO_TRANSACTION); + /** * Instantiates a new stereotype element listener on an editing domain. * @@ -83,18 +90,6 @@ public class StereotypeElementListener extends ResourceSetListenerImpl { this.finder = new StereotypeExtensionFinder(resourceSet); } - /** - * I am a pre-commit listener. Trigger listeners need to be able to react to - * these events. And they will be captured and automatically distributed again - * when the transaction commits (if it doesn't roll back). - * - * @return {@code true} - */ - @Override - public boolean isPrecommitOnly() { - return true; - } - @Override public Command transactionAboutToCommit(ResourceSetChangeEvent event) throws RollbackException { Command result = null; @@ -109,7 +104,7 @@ public class StereotypeElementListener extends ResourceSetListenerImpl { handleFilteredNotification(notification, chain); } - result = new AbstractCommand("Inject Stereotype Notifications") { + result = new AbstractCommand("Inject Stereotype Notifications") { //$NON-NLS-1$ private NotificationChain notifications; @Override @@ -125,38 +120,68 @@ public class StereotypeElementListener extends ResourceSetListenerImpl { @Override public void undo() { - dispatchAndInvert(); + // Schedule dispatch and inversion at post-commit time + undoRedoNotifications.add(this.notifications); } @Override public void redo() { - dispatchAndInvert(); + // Schedule dispatch and inversion at post-commit time + undoRedoNotifications.add(this.notifications); } private void dispatchAndInvert() { - notifications.dispatch(); - this.notifications = invertNotifications(notifications); + this.notifications.dispatch(); + invertNotifications(this.notifications); } + }; + } + + return result; + } - private NotificationChain invertNotifications(NotificationChain notifications) { - // The chain is implemented as an EList - @SuppressWarnings("unchecked") - EList<Notification> list = (EList<Notification>) notifications; + /** + * Invert the semantics of a chain of stereotype-application {@code notifications} + * in situ. This includes reversing the order in which they are dispatched. + * + * @param notifications + * notifications to invert + * @see StereotypeExtensionNotification#invert() + */ + private void invertNotifications(NotificationChain notifications) { + // The chain is implemented as an EList + @SuppressWarnings("unchecked") + EList<Notification> list = (EList<Notification>) notifications; - // Reverse the order - ECollections.reverse(list); + // Reverse the order + ECollections.reverse(list); - // And flip the type from applied <--> unapplied - for (Notification next : list) { - ((StereotypeExtensionNotification) next).invert(); - } + // And flip the type from applied <--> unapplied + for (Notification next : list) { + ((StereotypeExtensionNotification) next).invert(); + } + } - return notifications; + @Override + public void resourceSetChanged(ResourceSetChangeEvent event) { + if (Boolean.FALSE.equals(undoRedoOption.getValue(event.getTransaction().getOptions()))) { + // Not an undo/redo transaction? Purge any notifications that we may have + // had from a rolled-back undo or redo + undoRedoNotifications.clear(); + } else if (!undoRedoNotifications.isEmpty()) { + // It's an undo/redo transaction. Dispatch the (inverted) notifications + // that were deferred by the notification-injector command + try { + for (NotificationChain next : undoRedoNotifications) { + next.dispatch(); + invertNotifications(next); } - }; + } finally { + // Subsequent redo/undo of the injector command will collect + // these again + undoRedoNotifications.clear(); + } } - - return result; } /** |