blob: 407dc46e21c3781bf43d40a10263d059fca20bff [file] [log] [blame]
/*******************************************************************************
* 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;
}
}