1 package fi.vtt.simantics.procore.internal;
\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
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
28 import fi.vtt.simantics.procore.internal.SessionImplSocket.TaskHelper;
\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
35 UndoRedoSupportImpl(SessionImplSocket session) {
\r
36 this.session = session;
\r
37 this.managementSupport = session.getService(ManagementSupport.class);
\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
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
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
67 e.printStackTrace();
\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
79 writer.addMetadata(um.add(pum));
\r
80 um.setTypeAndRange(pum);
\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
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
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
106 e.printStackTrace();
\r
107 Logger.defaultLogError(e);
\r
108 th.throwableSet(e);
\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
118 final ArrayList<ExternalOperation> externalRedos = new ArrayList<ExternalOperation>();
\r
119 GraphSession.forExternals(ops, new FunctionImpl1<ExternalOperation, Boolean>() {
\r
122 public Boolean apply(final ExternalOperation op) {
\r
123 externalRedos.add(new ExternalOperation() {
\r
126 public void undo() {
\r
131 public void redo() {
\r
136 public boolean isDisposed() {
\r
137 return op.isDisposed();
\r
146 return new OperationImpl(id.get(), id.get(), externalRedos);
\r
149 private CommentMetadata getComment(long id) {
\r
150 Collection<CommentMetadata> metadata;
\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
160 private UndoMetadata getComment4Undo(long id) {
\r
161 Collection<UndoMetadata> metadata;
\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
172 public void undo(Operation op)
\r
173 throws DatabaseException {
\r
174 Collection<Operation> ops = new ArrayList<Operation>();
\r
180 public Operation getCurrent() {
\r
181 return session.state.getLastOperation();
\r
185 public int undo(Session session, int count)
\r
186 throws DatabaseException {
\r
187 return undoAndReturnOperations(session, count).size();
\r
191 public List<Operation> undoAndReturnOperations(Session session, int count)
\r
192 throws DatabaseException {
\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
202 public List<Operation> redo(Session session, int count)
\r
203 throws DatabaseException {
\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
213 public int undoTo(Session session, long changeSet)
\r
214 throws DatabaseException {
\r
215 if (!(session instanceof SessionImplDb) || changeSet < 1)
\r
217 SessionImplDb s = (SessionImplDb)session;
\r
218 long head = s.graphSession.getLastChangeSetId();
\r
219 int SIZE = (int)(head - changeSet);
\r
222 s.graphSession.undoContext.clear();
\r
223 Vector<Operation> ops = new Vector<Operation>(SIZE);
\r
225 long id = changeSet;
\r
226 for (int i = 0; i<SIZE; ++i) {
\r
228 Operation o = new OperationImpl(id, id);
\r
229 ops.setElementAt(o, i);
\r
235 public int initUndoListFrom(Session session, long changeSet)
\r
236 throws DatabaseException {
\r
237 if (!(session instanceof SessionImplDb) || changeSet < 1)
\r
239 SessionImplDb s = (SessionImplDb)session;
\r
240 long head = s.graphSession.getLastChangeSetId();
\r
241 int SIZE = (int)(head - changeSet + 1);
\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
248 s.graphSession.undoContext.clear();
\r
250 Iterator<CommitMetadata> it = metadata.iterator();
\r
251 long csid = changeSet;
\r
253 for (; csid<SIZE; ++csid) {
\r
254 CommitMetadata md = it.next();
\r
256 if (md.opid != 0 && md.opid != csid)
\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
264 return (int)(csid - first);
\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
275 public void subscribe(ChangeListener changeListener) {
\r
276 session.graphSession.undoContext.addChangeListener(changeListener);
\r
279 public void cancel(ChangeListener changelistener) {
\r
280 session.graphSession.undoContext.removeChangeListener(changelistener);
\r
284 public void addExternalOperation(WriteGraph graph, ExternalOperation op) {
\r
285 if (!(session instanceof SessionImplDb))
\r
287 SessionImplDb s = (SessionImplDb)session;
\r
288 s.graphSession.undoContext.addExternalOperation(op);
\r