]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/UndoRedoSupportImpl.java
Better emptying of trash bin
[simantics/platform.git] / bundles / org.simantics.db.procore / src / fi / vtt / simantics / procore / internal / UndoRedoSupportImpl.java
1 package fi.vtt.simantics.procore.internal;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Vector;
9
10 import org.simantics.db.Operation;
11 import org.simantics.db.Session;
12 import org.simantics.db.SessionVariables;
13 import org.simantics.db.UndoContext;
14 import org.simantics.db.WriteGraph;
15 import org.simantics.db.common.CommentMetadata;
16 import org.simantics.db.common.CommitMetadata;
17 import org.simantics.db.common.UndoMetadata;
18 import org.simantics.db.common.utils.Logger;
19 import org.simantics.db.exception.DatabaseException;
20 import org.simantics.db.impl.graph.WriteGraphImpl;
21 import org.simantics.db.impl.query.QueryProcessor.SessionTask;
22 import org.simantics.db.service.ExternalOperation;
23 import org.simantics.db.service.ManagementSupport;
24 import org.simantics.db.service.UndoRedoSupport;
25 import org.simantics.scl.runtime.function.FunctionImpl1;
26 import org.simantics.utils.DataContainer;
27
28 import fi.vtt.simantics.procore.internal.SessionImplSocket.TaskHelper;
29
30 public class UndoRedoSupportImpl implements UndoRedoSupport {
31     final private boolean DEBUG = SessionImplSocket.DEBUG;
32     final private SessionImplSocket session;
33     final ManagementSupport managementSupport;
34
35     UndoRedoSupportImpl(SessionImplSocket session) {
36         this.session = session;
37         this.managementSupport = session.getService(ManagementSupport.class);
38     }
39
40     @Override
41     public Operation undo(final Collection<Operation> ops) throws DatabaseException {
42         if (null == ops || ops.size() < 1)
43             throw new IllegalArgumentException("At least one operation must be defined.");
44         final Operation fop = (Operation)ops.toArray()[0];
45         final DataContainer<Long> id = new DataContainer<Long>(0L);
46         final TaskHelper th = new TaskHelper("Undo");
47         session.requestManager.scheduleWrite(new SessionTask(null, 0) {
48             @Override
49             public void run(int thread) {
50                 session.flushCounter = 0;
51                 session.clusterStream.reallyFlush();
52                 ClientChangesImpl cs = new ClientChangesImpl(session);
53                 if (session.clientChanges == null)
54                     session.clientChanges = cs;
55                 WriteGraphImpl writer = WriteGraphImpl.create(session.getQueryProvider2(), session.writeSupport, null);
56                 session.writeState = new WriteState<Object>(writer, th.writeTraits, th.sema, th.proc);
57                 try {
58                     SynchronizeContext context = new SynchronizeContext(session, cs, 1);
59                     boolean potentialConflicts = session.graphSession.undo(ops, context);
60                     if (potentialConflicts)
61                         th.throw_("Server thinks that there might be potential conflicts with this undo operation.");
62                     final boolean undo = true;
63                     if (!context.isOk(undo)) // this is a blocking operation
64                         th.throw_("Trouble with server reply.");
65                 } catch (Throwable e) {
66                     if (DEBUG)
67                         e.printStackTrace();
68                     th.throwableSet(e);
69                     th.sema.release();
70                     return;
71                 }
72                 try {
73                     writer.markUndoPoint(); // Undo should form it's own operation.
74                     // Add a comment to metadata.
75                     CommentMetadata cm = writer.getMetadata(CommentMetadata.class);
76                     UndoMetadata um = writer.getMetadata(UndoMetadata.class);
77                     UndoMetadata pum = getComment4Undo(fop.getId());
78                     if (null != pum) {
79                         writer.addMetadata(um.add(pum));
80                         um.setTypeAndRange(pum);
81                     }
82                     writer.addMetadata(um.add("Undo operation " + fop.getId() + "."));
83                     Operation ope = fop;
84                     if (ops.size() > 1) {
85                         writer.addMetadata(um.add("Undo " + ops.size() + " change sets."));
86                         writer.addMetadata(um.add("First change set was " + fop.getCSId() + "."));
87                         Operation lop = (Operation)ops.toArray()[ops.size()-1];
88                         writer.addMetadata(um.add("Last change set was " + lop.getCSId() + "."));
89                         ope = lop;
90                     }
91                     writer.addMetadata(cm.add(getComment(ope.getId())));
92                     if (null == pum || pum.getBeginCSId() == 0) {
93                         um.setTypeAndRange(false, ope.getId(), ope.getCSId());
94                         writer.addMetadata(um);
95                     }
96                     session.getQueryProvider2().performDirtyUpdates(writer);
97                     session.fireMetadataListeners(writer, cs);
98                     session.getQueryProvider2().performScheduledUpdates(writer);
99                     session.fireReactionsToSynchronize(cs);
100                     session.fireSessionVariableChange(SessionVariables.QUEUED_WRITES);
101                     session.printDiagnostics();
102                     long headChangeSetId = session.state.getHeadRevisionId();
103                     id.set(headChangeSetId+1);
104                 } catch (Throwable e) {
105                     if (DEBUG)
106                         e.printStackTrace();
107                     Logger.defaultLogError(e);
108                     th.throwableSet(e);
109                 } finally {
110                     cs.dispose();
111                 }
112             }
113         });
114         session.acquire(th.sema, th.writeTraits);
115         th.throwableCheck();
116         long headChangeSetId = session.state.getHeadRevisionId();
117         if (id.get() == headChangeSetId+1)
118             return null; // Empty undo operation;
119
120         final ArrayList<ExternalOperation> externalRedos = new ArrayList<ExternalOperation>();
121         GraphSession.forExternals(ops, new FunctionImpl1<ExternalOperation, Boolean>() {
122
123                 @Override
124                 public Boolean apply(final ExternalOperation op) {
125                         externalRedos.add(new ExternalOperation() {
126
127                                         @Override
128                                         public void undo() {
129                                                 op.redo();
130                                         }
131
132                                         @Override
133                                         public void redo() {
134                                                 op.undo();
135                                         }
136
137                                         @Override
138                                         public boolean isDisposed() {
139                                                 return op.isDisposed();
140                                         }
141
142                         });
143                         return true;
144                 }
145
146         });
147
148         return new OperationImpl(id.get(), id.get(), externalRedos);
149
150     }
151     private CommentMetadata getComment(long id) {
152         Collection<CommentMetadata> metadata;
153         try {
154             metadata = managementSupport.getMetadata(id, id, CommentMetadata.class);
155             if (metadata.size() > 0)
156                 return metadata.iterator().next();
157         } catch (Throwable t) {
158             Logger.defaultLogError(t);
159         }
160         return null;
161     }
162     private UndoMetadata getComment4Undo(long id) {
163         Collection<UndoMetadata> metadata;
164         try {
165             metadata = managementSupport.getMetadata(id, id, UndoMetadata.class);
166             if (metadata.size() > 0)
167                 return metadata.iterator().next();
168         } catch (Throwable t) {
169             Logger.defaultLogError(t);
170         }
171         return null;
172     }
173     @Override
174     public void undo(Operation op)
175     throws DatabaseException {
176         Collection<Operation> ops = new ArrayList<Operation>();
177         ops.add(op);
178         undo(ops);
179     }
180
181     @Override
182     public Operation getCurrent() {
183         return session.state.getLastOperation();
184     }
185
186     @Override
187     public int undo(Session session, int count)
188     throws DatabaseException {
189         return undoAndReturnOperations(session, count).size();
190     }
191
192     @Override
193     public List<Operation> undoAndReturnOperations(Session session, int count)
194     throws DatabaseException {
195         if ( count < 1)
196             return Collections.emptyList();
197         if (!(session instanceof SessionImplDb))
198             return Collections.emptyList();
199         SessionImplDb s = (SessionImplDb)session;
200         return s.graphSession.undoContext.undo(this, count);
201     }
202
203     @Override
204     public List<Operation> redo(Session session, int count)
205     throws DatabaseException {
206         if ( count < 1)
207             return Collections.emptyList();
208         if (!(session instanceof SessionImplDb))
209             return Collections.emptyList();
210         SessionImplDb s = (SessionImplDb)session;
211         return s.graphSession.undoContext.redo(this, count);
212     }
213
214     @Override
215     public int undoTo(Session session, long changeSet)
216         throws DatabaseException {
217         if (!(session instanceof SessionImplDb) || changeSet < 1)
218             return 0;
219         SessionImplDb s = (SessionImplDb)session;
220         long head = s.graphSession.getLastChangeSetId();
221         int SIZE = (int)(head - changeSet);
222         if (SIZE < 1)
223             return 0;
224         s.graphSession.undoContext.clear();
225         Vector<Operation> ops = new Vector<Operation>(SIZE);
226         ops.setSize(SIZE);
227         long id = changeSet;
228         for (int i = 0; i<SIZE; ++i) {
229             ++id;
230             Operation o = new OperationImpl(id, id);
231             ops.setElementAt(o, i);
232         }
233         undo(ops);
234         return SIZE;
235     }
236     @Override
237     public int initUndoListFrom(Session session, long changeSet)
238         throws DatabaseException {
239         if (!(session instanceof SessionImplDb) || changeSet < 1)
240             return 0;
241         SessionImplDb s = (SessionImplDb)session;
242         long head = s.graphSession.getLastChangeSetId();
243         int SIZE = (int)(head - changeSet + 1);
244         if (SIZE < 1)
245             return 0;
246         ManagementSupport ms = session.getService(ManagementSupport.class);
247         Collection<CommitMetadata> metadata = ms.getMetadata(changeSet, head, CommitMetadata.class);
248         if (metadata.size() != SIZE)
249             return 0;
250         s.graphSession.undoContext.clear();
251         long first = 0;
252         Iterator<CommitMetadata> it = metadata.iterator();
253         long csid = changeSet;
254         SIZE += csid;
255         for (; csid<SIZE; ++csid) {
256             CommitMetadata md = it.next();
257             if (first == 0) {
258                 if (md.opid != 0 && md.opid != csid)
259                     continue;
260                 first = csid;
261             }
262             long id = md.opid != 0 ? md.opid : csid;
263             Operation op = new OperationImpl(id, csid);
264             s.graphSession.undoContext.commitOk(op);
265         }
266         return (int)(csid - first);
267     }
268     @Override
269     public UndoContext getUndoContext(Session session) {
270         if (session instanceof SessionImplSocket) {
271             GraphSession graphSession = ((SessionImplSocket)session).graphSession;
272             return graphSession != null ? graphSession.undoContext : null;
273         }
274         return null;
275     }
276     @Override
277     public void subscribe(ChangeListener changeListener) {
278         session.graphSession.undoContext.addChangeListener(changeListener);
279     }
280     @Override
281     public void cancel(ChangeListener changelistener) {
282         session.graphSession.undoContext.removeChangeListener(changelistener);
283     }
284
285     @Override
286     public void addExternalOperation(WriteGraph graph, ExternalOperation op) {
287         if (!(session instanceof SessionImplDb))
288             return;
289         SessionImplDb s = (SessionImplDb)session;
290         s.graphSession.undoContext.addExternalOperation(op);
291     }
292
293 }