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