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