package fi.vtt.simantics.procore.internal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Vector; import org.simantics.db.Operation; import org.simantics.db.Session; import org.simantics.db.SessionVariables; import org.simantics.db.UndoContext; import org.simantics.db.WriteGraph; import org.simantics.db.common.CommentMetadata; import org.simantics.db.common.CommitMetadata; import org.simantics.db.common.UndoMetadata; import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.impl.graph.WriteGraphImpl; import org.simantics.db.impl.query.QueryProcessor.SessionTask; import org.simantics.db.service.ExternalOperation; import org.simantics.db.service.ManagementSupport; import org.simantics.db.service.UndoRedoSupport; import org.simantics.scl.runtime.function.FunctionImpl1; import org.simantics.utils.DataContainer; import fi.vtt.simantics.procore.internal.SessionImplSocket.TaskHelper; public class UndoRedoSupportImpl implements UndoRedoSupport { final private boolean DEBUG = SessionImplSocket.DEBUG; final private SessionImplSocket session; final ManagementSupport managementSupport; UndoRedoSupportImpl(SessionImplSocket session) { this.session = session; this.managementSupport = session.getService(ManagementSupport.class); } @Override public Operation undo(final Collection ops) throws DatabaseException { if (null == ops || ops.size() < 1) throw new IllegalArgumentException("At least one operation must be defined."); final Operation fop = (Operation)ops.toArray()[0]; final DataContainer id = new DataContainer(0L); final TaskHelper th = new TaskHelper("Undo"); session.requestManager.scheduleWrite(new SessionTask(null, 0) { @Override public void run(int thread) { session.flushCounter = 0; session.clusterStream.reallyFlush(); ClientChangesImpl cs = new ClientChangesImpl(session); if (session.clientChanges == null) session.clientChanges = cs; WriteGraphImpl writer = WriteGraphImpl.create(session.getQueryProvider2(), session.writeSupport, null); session.writeState = new WriteState(writer, th.writeTraits, th.sema, th.proc); try { SynchronizeContext context = new SynchronizeContext(session, cs, 1); boolean potentialConflicts = session.graphSession.undo(ops, context); if (potentialConflicts) th.throw_("Server thinks that there might be potential conflicts with this undo operation."); final boolean undo = true; if (!context.isOk(undo)) // this is a blocking operation th.throw_("Trouble with server reply."); } catch (Throwable e) { if (DEBUG) e.printStackTrace(); th.throwableSet(e); th.sema.release(); return; } try { writer.markUndoPoint(); // Undo should form it's own operation. // Add a comment to metadata. CommentMetadata cm = writer.getMetadata(CommentMetadata.class); UndoMetadata um = writer.getMetadata(UndoMetadata.class); UndoMetadata pum = getComment4Undo(fop.getId()); if (null != pum) { writer.addMetadata(um.add(pum)); um.setTypeAndRange(pum); } writer.addMetadata(um.add("Undo operation " + fop.getId() + ".")); Operation ope = fop; if (ops.size() > 1) { writer.addMetadata(um.add("Undo " + ops.size() + " change sets.")); writer.addMetadata(um.add("First change set was " + fop.getCSId() + ".")); Operation lop = (Operation)ops.toArray()[ops.size()-1]; writer.addMetadata(um.add("Last change set was " + lop.getCSId() + ".")); ope = lop; } writer.addMetadata(cm.add(getComment(ope.getId()))); if (null == pum || pum.getBeginCSId() == 0) { um.setTypeAndRange(false, ope.getId(), ope.getCSId()); writer.addMetadata(um); } session.getQueryProvider2().performDirtyUpdates(writer); session.fireMetadataListeners(writer, cs); session.getQueryProvider2().performScheduledUpdates(writer); session.fireReactionsToSynchronize(cs); session.fireSessionVariableChange(SessionVariables.QUEUED_WRITES); session.printDiagnostics(); long headChangeSetId = session.state.getHeadRevisionId(); id.set(headChangeSetId+1); } catch (Throwable e) { if (DEBUG) e.printStackTrace(); Logger.defaultLogError(e); th.throwableSet(e); } } }); session.acquire(th.sema, th.writeTraits); th.throwableCheck(); long headChangeSetId = session.state.getHeadRevisionId(); if (id.get() == headChangeSetId+1) return null; // Empty undo operation; final ArrayList externalRedos = new ArrayList(); GraphSession.forExternals(ops, new FunctionImpl1() { @Override public Boolean apply(final ExternalOperation op) { externalRedos.add(new ExternalOperation() { @Override public void undo() { op.redo(); } @Override public void redo() { op.undo(); } @Override public boolean isDisposed() { return op.isDisposed(); } }); return true; } }); return new OperationImpl(id.get(), id.get(), externalRedos); } private CommentMetadata getComment(long id) { Collection metadata; try { metadata = managementSupport.getMetadata(id, id, CommentMetadata.class); if (metadata.size() > 0) return metadata.iterator().next(); } catch (Throwable t) { Logger.defaultLogError(t); } return null; } private UndoMetadata getComment4Undo(long id) { Collection metadata; try { metadata = managementSupport.getMetadata(id, id, UndoMetadata.class); if (metadata.size() > 0) return metadata.iterator().next(); } catch (Throwable t) { Logger.defaultLogError(t); } return null; } @Override public void undo(Operation op) throws DatabaseException { Collection ops = new ArrayList(); ops.add(op); undo(ops); } @Override public Operation getCurrent() { return session.state.getLastOperation(); } @Override public int undo(Session session, int count) throws DatabaseException { return undoAndReturnOperations(session, count).size(); } @Override public List undoAndReturnOperations(Session session, int count) throws DatabaseException { if ( count < 1) return Collections.emptyList(); if (!(session instanceof SessionImplDb)) return Collections.emptyList(); SessionImplDb s = (SessionImplDb)session; return s.graphSession.undoContext.undo(this, count); } @Override public List redo(Session session, int count) throws DatabaseException { if ( count < 1) return Collections.emptyList(); if (!(session instanceof SessionImplDb)) return Collections.emptyList(); SessionImplDb s = (SessionImplDb)session; return s.graphSession.undoContext.redo(this, count); } @Override public int undoTo(Session session, long changeSet) throws DatabaseException { if (!(session instanceof SessionImplDb) || changeSet < 1) return 0; SessionImplDb s = (SessionImplDb)session; long head = s.graphSession.getLastChangeSetId(); int SIZE = (int)(head - changeSet); if (SIZE < 1) return 0; s.graphSession.undoContext.clear(); Vector ops = new Vector(SIZE); ops.setSize(SIZE); long id = changeSet; for (int i = 0; i metadata = ms.getMetadata(changeSet, head, CommitMetadata.class); if (metadata.size() != SIZE) return 0; s.graphSession.undoContext.clear(); long first = 0; Iterator it = metadata.iterator(); long csid = changeSet; SIZE += csid; for (; csid