| /******************************************************************************* |
| * Copyright (c) 2004-2008 Andras Schmidt, Andras Balogh, Istvan Rath and Daniel Varro |
| * 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: |
| * Andras Schmidt, Andras Balogh, Istvan Rath - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.viatra2.core.simple; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Stack; |
| |
| import org.eclipse.viatra2.core.EDeleteSemantics; |
| import org.eclipse.viatra2.core.ICoreNotificationListener; |
| import org.eclipse.viatra2.core.IEntity; |
| import org.eclipse.viatra2.core.IModelElement; |
| import org.eclipse.viatra2.core.IModelSpace; |
| import org.eclipse.viatra2.core.IRelation; |
| import org.eclipse.viatra2.core.IUndoManager; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObject; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectCreateEntity; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectCreateInstanceOf; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectCreateRelation; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectCreateSupertypeOf; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectDeleteContainment; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectDeleteEntity; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectDeleteInstanceOf; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectDeleteRelation; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectDeleteSupertypeOf; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectMoveTo; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectSetName; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectSetRelationFrom; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectSetRelationTo; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectSetValue; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectSetViewInfo; |
| import org.eclipse.viatra2.core.notification.ICoreNotificationObjectUserMark; |
| import org.eclipse.viatra2.core.notification.NotificationType; |
| import org.eclipse.viatra2.core.simple.notification.NotificationObject; |
| import org.eclipse.viatra2.core.simple.notification.NotificationObjectAtomicStepReady; |
| import org.eclipse.viatra2.core.simple.notification.NotificationObjectUserMark; |
| import org.eclipse.viatra2.errors.VPMCoreException; |
| import org.eclipse.viatra2.errors.VPMCoreNullParameterException; |
| import org.eclipse.viatra2.errors.VPMCoreRuntimeException; |
| import org.eclipse.viatra2.errors.VPMRuntimeException; |
| import org.eclipse.viatra2.logger.Logger; |
| |
| /** |
| * This class defines the undo support for the Simple Viatra core. This class is |
| * the undoManager interface of the Simple Core implementation, and the listener |
| * of undo notification objects too. |
| * |
| * @author Andras Schmidt |
| * |
| * Modified on 2006.08.14 by Istvan Rath: - clean up unnecessary code - |
| * add preliminary transaction support (don't throw exceptions) |
| * |
| */ |
| public class SimpleUndoManager implements IUndoManager, |
| ICoreNotificationListener { |
| IModelSpace modelSpace; |
| |
| SimpleModelManager mManager; |
| |
| Logger logger; |
| |
| private Stack<ICoreNotificationObject> undoStack; |
| |
| boolean underUndo; |
| |
| boolean checkConsistencyOn = false; |
| |
| /** |
| * Initializes the undo manager. |
| * |
| * @param l |
| * - the framework logger |
| * @param m |
| * - the owning modelspace |
| * @throws VPMRuntimeException |
| */ |
| public void init(Logger l, IModelSpace m) throws VPMRuntimeException { |
| logger = l; |
| modelSpace = m; |
| mManager = (SimpleModelManager) modelSpace.getModelManager(); |
| } |
| |
| /** |
| * Registers a new undo block with a descriptive name. (Marks the point of |
| * state with the given string.) |
| * |
| * @param name |
| * - name of undo block |
| */ |
| public void nextUndoBlock(String name) { |
| NotificationObject no = new NotificationObjectUserMark(name); |
| undoStack.push(no); |
| } |
| |
| /** |
| * Checks whether the undo buffer is empty |
| * |
| * @return true, if there are operations in the undo buffer |
| */ |
| public boolean canUndo() { |
| return !undoStack.empty(); |
| } |
| |
| /** |
| * Flushes the undo buffer. |
| * |
| */ |
| public void flushUndoBuffer() { |
| undoStack = new Stack<ICoreNotificationObject>(); |
| } |
| |
| /** |
| * Undoes the last operation |
| */ |
| public void undo() { |
| underUndo = true; |
| boolean empty = false; |
| boolean wasChange = false; |
| ICoreNotificationObject note = null; |
| do { |
| if (undoStack.isEmpty()) { |
| empty = true; |
| break; |
| } |
| note = undoStack.pop(); |
| try { |
| wasChange = undoStep(note) || wasChange; |
| } catch (VPMCoreException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| } while (!(note.getActionTypeEnum() == NotificationType.ACTION_ATOMIC_STEP_READY && wasChange)); |
| if (!empty) |
| undoStack.push(note); |
| underUndo = false; |
| // if(checkConsistencyOn)
|
| // ConsistencyChecker.check(modelSpace);
|
| } |
| |
| /** |
| * Undoes all operations made after the last checkpoint with given name. |
| * |
| * @param markName |
| */ |
| public void undo(String markName) { |
| underUndo = true; |
| // we are in an atomic step, we must undo until first atomic_step_ready
|
| boolean wasChange = false; |
| // we have found the checkpoint mark, no more undo needed
|
| boolean toLeave = false; |
| ICoreNotificationObject note = null; |
| do { |
| if (undoStack.isEmpty()) { |
| break; |
| } |
| note = undoStack.pop(); |
| try { |
| wasChange = undoStep(note) || wasChange; |
| } catch (VPMCoreException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| switch(note.getActionTypeEnum()){ |
| |
| case ACTION_ATOMIC_STEP_READY: |
| wasChange = false; |
| break; |
| |
| case USER_MARK: |
| ICoreNotificationObjectUserMark _note = (ICoreNotificationObjectUserMark) note; |
| String name = _note.getMarkName(); |
| if (name.equals(markName)) |
| toLeave = true; |
| break; |
| |
| } |
| /*if (note.getActionType().equals( |
| ICoreNotificationObject.ACTION_ATOMIC_STEP_READY)) { |
| wasChange = false; |
| } |
| if (note.getActionType().equals(ICoreNotificationObject.USER_MARK)) { |
| ICoreNotificationObjectUserMark _note = (ICoreNotificationObjectUserMark) note; |
| String name = _note.getMarkName(); |
| if (name.equals(markName)) |
| toLeave = true; |
| }*/ |
| } while (!toLeave || wasChange); |
| undoStack.push(new NotificationObjectAtomicStepReady()); |
| underUndo = false; |
| } |
| |
| /** |
| * Undoes all operations made after the n. checkpoint with given name. |
| * |
| * @param markName |
| * @param n |
| */ |
| public void undo(String markName, int n) { |
| for (int i = 0; i < n; ++i) |
| undo(markName); |
| } |
| |
| /** |
| * Undoes the last n operations |
| * |
| * @param n |
| * number of operations to be undone. |
| */ |
| public void undo(int n) { |
| for (int i = 0; i < n; ++i) |
| undo(); |
| } |
| |
| private boolean undoStep(ICoreNotificationObject note) |
| throws VPMCoreException { |
| // FIXME add transaction support!
|
| NotificationType action = note.getActionTypeEnum(); |
| |
| switch(action){ |
| |
| case ACTION_ATOMIC_STEP_READY: |
| return false; |
| case ACTION_CREATE_ENTITY: |
| ICoreNotificationObjectCreateEntity _note = (ICoreNotificationObjectCreateEntity) note; |
| |
| IEntity ent = _note.getCreated(); |
| try { |
| mManager |
| .deleteEntity(ent, EDeleteSemantics.DELETE_SEMANTICS_FORCE); |
| } catch (Exception e) { |
| throw new VPMCoreRuntimeException("undo step failed" |
| + e.toString() + e.getStackTrace()); |
| } |
| return true; |
| case ACTION_DELETE_ENTITY: |
| ICoreNotificationObjectDeleteEntity _note1 = (ICoreNotificationObjectDeleteEntity) note; |
| IEntity ent1 = _note1.getDeleted(); |
| IEntity parent = _note1.getParent(); |
| mManager |
| .undoCreateEntity((SimpleEntity) ent1, (SimpleEntity) parent); |
| return true; |
| case ACTION_CREATE_RELATION: |
| ICoreNotificationObjectCreateRelation _note2 = (ICoreNotificationObjectCreateRelation) note; |
| IRelation rel = _note2.getNewRelation(); |
| try { |
| mManager.deleteRelation(rel, |
| EDeleteSemantics.DELETE_SEMANTICS_FORCE); |
| } catch (Exception e) { |
| throw new VPMCoreRuntimeException("undo step failed" |
| + e.toString() + e.getStackTrace()); |
| } |
| return true; |
| case ACTION_DELETE_RELATION: |
| ICoreNotificationObjectDeleteRelation _note3 = (ICoreNotificationObjectDeleteRelation) note; |
| mManager.undoCreateRelation((SimpleRelation) _note3.getDeleted(), |
| (SimpleModelElement) _note3.getFrom(), |
| (SimpleModelElement) _note3.getTo()); |
| return true; |
| case ACTION_CREATE_INSTANCEOF: |
| ICoreNotificationObjectCreateInstanceOf _note4 = (ICoreNotificationObjectCreateInstanceOf) note; |
| SimpleModelElement sup = (SimpleModelElement) _note4.getType(); |
| SimpleModelElement sub = (SimpleModelElement) _note4.getInstance(); |
| try { |
| mManager.deleteInstanceOfEditor(sup, sub); |
| } catch (VPMCoreNullParameterException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_DELETE_INSTANCEOF: |
| ICoreNotificationObjectDeleteInstanceOf _note5 = (ICoreNotificationObjectDeleteInstanceOf) note; |
| sup = (SimpleModelElement) _note5.getType(); |
| sub = (SimpleModelElement) _note5.getInstance(); |
| try { |
| mManager.newInstanceOfEditor(sup, sub); |
| } catch (Exception e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_CREATE_SUPERTYPEOF: |
| ICoreNotificationObjectCreateSupertypeOf _note6 = (ICoreNotificationObjectCreateSupertypeOf) note; |
| sup = (SimpleModelElement) _note6.getSuper(); |
| sub = (SimpleModelElement) _note6.getSub(); |
| try { |
| mManager.deleteSupertypeOfEditor(sup, sub); |
| } catch (VPMCoreNullParameterException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_DELETE_SUPERTYPEOF: |
| ICoreNotificationObjectDeleteSupertypeOf _note7 = (ICoreNotificationObjectDeleteSupertypeOf) note; |
| sup = (SimpleModelElement) _note7.getSuper(); |
| sub = (SimpleModelElement) _note7.getSub(); |
| try { |
| mManager.newSupertypeOfEditor(sup, sub); |
| } catch (VPMCoreException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_DELETE_CONTAINMENT: |
| ICoreNotificationObjectDeleteContainment _note8 = (ICoreNotificationObjectDeleteContainment) note; |
| |
| SimpleEntity container = (SimpleEntity) _note8.getParent(); |
| SimpleEntity child = (SimpleEntity) _note8.getChild(); |
| try { |
| mManager.undoCreateContainment(container, child); |
| } catch (VPMCoreNullParameterException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_SET_VALUE: |
| ICoreNotificationObjectSetValue _note9 = (ICoreNotificationObjectSetValue) note; |
| ent = (SimpleEntity) _note9.getEntity(); |
| String oldValue = _note9.getOldValue(); |
| try { |
| mManager.setValue(ent, oldValue); |
| } catch (VPMCoreNullParameterException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_SET_NAME: |
| ICoreNotificationObjectSetName _note10 = (ICoreNotificationObjectSetName) note; |
| SimpleModelElement elem = (SimpleModelElement) _note10.getElement(); |
| String oldName = _note10.getOldName(); |
| try { |
| mManager.setName(elem, oldName); |
| } catch (VPMCoreNullParameterException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_MOVE_ELEMENT_TO: |
| ICoreNotificationObjectMoveTo _note11 = (ICoreNotificationObjectMoveTo) note; |
| ent = (SimpleEntity) _note11.getElement(); |
| SimpleEntity from = (SimpleEntity) _note11.getOldContainer(); |
| try { |
| mManager.moveEntityTo(ent, from); |
| } catch (VPMCoreNullParameterException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_SET_RELATION_FROM: |
| ICoreNotificationObjectSetRelationFrom _note12 = (ICoreNotificationObjectSetRelationFrom) note; |
| rel = (SimpleRelation) _note12.getRelation(); |
| SimpleModelElement oldFrom = (SimpleModelElement) _note12 |
| .getOldFrom(); |
| try { |
| mManager.setRelationFrom(rel, oldFrom); |
| } catch (VPMCoreNullParameterException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_SET_RELATION_TO: |
| ICoreNotificationObjectSetRelationTo _note13 = (ICoreNotificationObjectSetRelationTo) note; |
| rel = (SimpleRelation) _note13.getRelation(); |
| SimpleModelElement oldTo = (SimpleModelElement) _note13.getOldTo(); |
| try { |
| mManager.setRelationTo(rel, oldTo); |
| } catch (VPMCoreNullParameterException e) { |
| logger.fatal("Error performing undo; message: " |
| + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| return true; |
| case ACTION_SET_VIEW_INFO: |
| undoSetViewInfo((ICoreNotificationObjectSetViewInfo) note); |
| return true; |
| case USER_MARK: |
| return false; |
| } |
| /* |
| * Added a comment to this code to stop the undo manager from throwing unnecessary error messages |
| * which slowes down undo operations. |
| throw new VPMCoreRuntimeException("undo step not defined for step:" |
| + note.toString()); |
| */ |
| //logger.warning("undo step not defined for step:"+ note.toString()); |
| return false; |
| } |
| |
| void undoSetViewInfo(ICoreNotificationObjectSetViewInfo note) { |
| IModelElement me = note.getElement(); |
| String oldViewInfo = note.getOldViewInfo(); |
| try { |
| mManager.setViewInfo(me, oldViewInfo); |
| } catch (VPMCoreException e) { |
| logger.fatal("Error performing undo; message: " + e.getMessage()); |
| // e.printStackTrace();
|
| } |
| } |
| |
| /** |
| * Gets a list of undoable operations currently in the undo buffer. |
| * |
| * @return array of operation names |
| */ |
| public List<String> getUndoableList() { |
| ArrayList<String> undoList = new ArrayList<String>(); |
| Iterator<ICoreNotificationObject> it = undoStack.iterator(); |
| while (it.hasNext()) { |
| ICoreNotificationObject note = it.next(); |
| undoList.add(note.toString()); |
| } |
| return undoList; |
| } |
| |
| public SimpleUndoManager() { |
| underUndo = false; |
| undoStack = new Stack<ICoreNotificationObject>(); |
| } |
| |
| /** |
| * This method listens all actions and logs them into a stack for undo |
| */ |
| public void actionPerformed(ICoreNotificationObject notification) { |
| // if(notification.getActionType()==ICoreNotificationObject. |
| // ACTION_ATOMIC_STEP_READY)
|
| // {
|
| // if(checkConsistencyOn)
|
| // ConsistencyChecker.check(modelSpace);
|
| // }
|
| NotificationType type = notification.getActionTypeEnum(); |
| switch(type){ |
| |
| case TA_SUBTRANSACTION_BEGIN: |
| case TA_SUBTRANSACTION_END: |
| case TA_TRANSACTION_BEGIN: |
| case TA_TRANSACTION_END: |
| case TA_UNDOABLE_TRANSACTION_BEGIN: |
| return; |
| } |
| /*if (type.equals(ICoreNotificationObject.TA_SUBTRANSACTION_BEGIN) |
| || type.equals(ICoreNotificationObject.TA_SUBTRANSACTION_END) |
| || type.equals(ICoreNotificationObject.TA_TRANSACTION_BEGIN) |
| || type.equals(ICoreNotificationObject.TA_TRANSACTION_END) |
| || type |
| .equals(ICoreNotificationObject.TA_UNDOABLE_TRANSACTION_BEGIN)) |
| return;*/ |
| if (!underUndo) |
| undoStack.add(notification); |
| } |
| |
| /** |
| * Returns the category of this listener. |
| * |
| * @return category |
| */ |
| public int getListenerCategory() { |
| return ICoreNotificationListener.UNDO_LISTENER; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.viatra2.core.IUndoManager#getUndoList(int) |
| */ |
| public List<String> getUndoList(int n) { |
| int count = 0; |
| ArrayList<String> ret = new ArrayList<String>(); |
| int last = undoStack.size(); |
| while (count < n) { |
| ICoreNotificationObject note = undoStack |
| .get(--last); |
| if (note.getActionTypeEnum().equals(NotificationType.USER_MARK)) { |
| ret.add(((ICoreNotificationObjectUserMark) note).getMarkName()); |
| count++; |
| } |
| if (last == 0) |
| break; |
| } |
| return ret; |
| } |
| } |