]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/DebuggerCanvas.java
Some SCL functions to control graphical debugger
[simantics/platform.git] / bundles / org.simantics.debug.graphical / src / org / simantics / debug / graphical / DebuggerCanvas.java
1 package org.simantics.debug.graphical;
2
3 import java.awt.Color;
4 import java.awt.GradientPaint;
5 import java.awt.Graphics;
6 import java.awt.Graphics2D;
7 import java.awt.RenderingHints;
8 import java.awt.datatransfer.Transferable;
9 import java.awt.datatransfer.UnsupportedFlavorException;
10 import java.awt.dnd.DnDConstants;
11 import java.awt.dnd.DropTarget;
12 import java.awt.dnd.DropTargetAdapter;
13 import java.awt.dnd.DropTargetDropEvent;
14 import java.awt.event.KeyAdapter;
15 import java.awt.event.KeyEvent;
16 import java.awt.event.MouseAdapter;
17 import java.awt.event.MouseEvent;
18 import java.awt.event.MouseMotionAdapter;
19 import java.awt.event.MouseWheelEvent;
20 import java.awt.event.MouseWheelListener;
21 import java.awt.geom.AffineTransform;
22 import java.awt.geom.Point2D;
23 import java.awt.geom.Rectangle2D;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Random;
28
29 import javax.swing.JPanel;
30 import javax.swing.SwingUtilities;
31
32 import org.eclipse.core.runtime.IAdaptable;
33 import org.eclipse.jface.viewers.IStructuredSelection;
34 import org.simantics.Simantics;
35 import org.simantics.db.ChangeSet;
36 import org.simantics.db.ChangeSetIdentifier;
37 import org.simantics.db.ReadGraph;
38 import org.simantics.db.Resource;
39 import org.simantics.db.Session;
40 import org.simantics.db.Statement;
41 import org.simantics.db.common.request.ReadRequest;
42 import org.simantics.db.common.utils.NameUtils;
43 import org.simantics.db.exception.DatabaseException;
44 import org.simantics.db.service.ManagementSupport;
45 import org.simantics.debug.graphical.layout.ExtensionLayoutAlgorithm;
46 import org.simantics.debug.graphical.layout.LayoutGraph;
47 import org.simantics.debug.graphical.model.Edge;
48 import org.simantics.debug.graphical.model.LabelContent;
49 import org.simantics.debug.graphical.model.Node;
50 import org.simantics.debug.graphical.model.NodeData;
51 import org.simantics.layer0.Layer0;
52 import org.simantics.scl.runtime.SCLContext;
53 import org.simantics.scl.runtime.function.Function;
54 import org.simantics.ui.dnd.LocalObjectTransfer;
55 import org.simantics.ui.dnd.LocalObjectTransferable;
56 import org.simantics.ui.selection.AnyResource;
57 import org.simantics.ui.selection.WorkbenchSelectionElement;
58
59 import gnu.trove.list.array.TDoubleArrayList;
60 import gnu.trove.map.hash.THashMap;
61 import gnu.trove.map.hash.TObjectIntHashMap;
62
63 public class DebuggerCanvas extends JPanel {
64
65     private static final long serialVersionUID = -718678297301786379L;
66     
67     ArrayList<Node> nodes = new ArrayList<Node>();
68     THashMap<Resource, Node> nodeMap = new THashMap<Resource, Node>(); 
69     ArrayList<Edge> edges = new ArrayList<Edge>();
70     ArrayList<Node> extensionNodes = new ArrayList<Node>();
71     ArrayList<Edge> extensionEdges = new ArrayList<Edge>();
72     ArrayList<Edge> addedEdges = new ArrayList<Edge>();
73     ArrayList<Edge> removedEdges = new ArrayList<Edge>();
74     double canvasPosX = 0.0;
75     double canvasPosY = 0.0;
76     double canvasZoom = 1.0;
77     boolean extensionMode = false;
78     Random random = new Random();
79     public Function statementFilter;
80     
81     LabelingPreferences labelingPreferences = 
82             new LabelingPreferences();
83     
84     public void setStatementFilter(Function statementFilter) {
85         this.statementFilter = statementFilter;
86     }
87     
88     public void removeStatementFilter() {
89         this.statementFilter = null;
90     }
91     
92     @Override
93     public void paint(Graphics _g) {
94         Graphics2D g = (Graphics2D)_g;
95         
96         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
97                 RenderingHints.VALUE_ANTIALIAS_ON);
98         g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, 
99                 RenderingHints.VALUE_FRACTIONALMETRICS_ON);
100         g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 
101                 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
102         
103         g.setPaint(new GradientPaint(0.0f, 0.0f, new Color(200, 200, 200), getWidth(), getHeight(), Color.WHITE));
104         g.fill(new Rectangle2D.Double(0, 0, getWidth(), getHeight()));
105         g.setColor(Color.BLACK);
106         g.setTransform(new AffineTransform(
107                 canvasZoom, 0.0, 
108                 0.0, canvasZoom, 
109                 -canvasPosX*canvasZoom, -canvasPosY*canvasZoom));
110         for(Node node : nodes)
111             node.render(g);
112         for(Edge edge : edges)
113             edge.render(g);
114         if(extensionMode) {
115             for(Node node : extensionNodes)
116                 node.render(g);
117             for(Edge edge : extensionEdges)
118                 edge.render(g);
119         }
120         g.setColor(Color.GREEN);
121         for(Edge edge : addedEdges)
122             edge.render(g);
123         g.setColor(Color.RED);
124         for(Edge edge : removedEdges)
125             edge.render(g);
126     }
127     
128     public Node pick(double x, double y) {
129         for(Node node : nodes)
130             if(node.pick(x, y))
131                 return node;
132         return null;
133     }
134     
135     public Node pickExtension(double x, double y) {
136         for(Node node : extensionNodes)
137             if(node.pick(x, y))
138                 return node;
139         return null;
140     }
141     
142     {
143         addMouseListener(new MouseAdapter() {
144             @Override
145             public void mousePressed(MouseEvent e) {                
146                 double x = canvasPosX+e.getX()/canvasZoom;
147                 double y = canvasPosY+e.getY()/canvasZoom;
148                 if(e.getButton() == MouseEvent.BUTTON1)
149                     handleMouseLeftPressed(x, y);
150                 else if(e.getButton() == MouseEvent.BUTTON2)
151                     handleMouseMiddlePressed(x, y);
152             }
153             @Override
154             public void mouseMoved(MouseEvent e) {
155                 handleMouseMoved(canvasPosX+e.getX()/canvasZoom, 
156                         canvasPosY+e.getY()/canvasZoom);
157             }
158             @Override
159             public void mouseReleased(MouseEvent e) {
160                 handleMouseReleased(canvasPosX+e.getX()/canvasZoom, 
161                         canvasPosY+e.getY()/canvasZoom);
162             }
163             @Override
164             public void mouseWheelMoved(MouseWheelEvent e) {
165                 double x = canvasPosX+e.getX()/canvasZoom;
166                 double y = canvasPosY+e.getY()/canvasZoom;
167                 handleMouseWheelMoved(x, y, e.getWheelRotation());
168             }
169         });
170         addMouseMotionListener(new MouseMotionAdapter() {
171             @Override
172             public void mouseDragged(MouseEvent e) {
173                 handleMouseMoved(canvasPosX+e.getX()/canvasZoom, 
174                         canvasPosY+e.getY()/canvasZoom);
175             }            
176         });
177         addMouseWheelListener(new MouseWheelListener() {            
178             @Override
179             public void mouseWheelMoved(MouseWheelEvent e) {
180                 double x = canvasPosX+e.getX()/canvasZoom;
181                 double y = canvasPosY+e.getY()/canvasZoom;
182                 handleMouseWheelMoved(x, y, e.getWheelRotation());
183             }
184         });
185         addKeyListener(new UsefulKeyAdapter(new KeyAdapter() {
186             @Override
187             public void keyPressed(KeyEvent e) {
188                 DebuggerCanvas.this.keyPressed(e);
189             }
190             @Override
191             public void keyReleased(KeyEvent e) {
192                 DebuggerCanvas.this.keyReleased(e);
193             }
194         }));
195         DropTarget dropTarget = new DropTarget(this, new DropTargetAdapter() {
196             @Override
197             public void drop(DropTargetDropEvent dtde) {
198                 try {
199                     Transferable transferable = dtde.getTransferable();
200                     
201                     if( transferable.isDataFlavorSupported( 
202                             LocalObjectTransferable.FLAVOR ) ) {
203                         dtde.acceptDrop( DnDConstants.ACTION_MOVE );
204                         
205                         transferable.getTransferData(LocalObjectTransferable.FLAVOR );
206                         Object obj = LocalObjectTransfer.getTransfer().getObject();
207                         double x = canvasPosX+dtde.getLocation().getX()/canvasZoom;
208                         double y = canvasPosY+dtde.getLocation().getY()/canvasZoom;
209                         handleDrop(x, y, obj);
210                         
211                         dtde.getDropTargetContext().dropComplete( true );
212                     }
213                     else {
214                         dtde.rejectDrop();
215                     }
216                 } catch( IOException exception ) {
217                     exception.printStackTrace();
218                     dtde.rejectDrop();
219                 } catch( UnsupportedFlavorException ufException ) {
220                     ufException.printStackTrace();
221                     dtde.rejectDrop();
222                 }
223             }     
224         });
225     }    
226     
227     public void keyPressed(KeyEvent e) {
228         switch(e.getKeyCode()) {
229         case KeyEvent.VK_1:
230             zoomToFit();
231             break;
232         case KeyEvent.VK_L:
233             layoutGraph();
234             break;
235         case KeyEvent.VK_CONTROL:
236             if(!extensionMode) {
237                 initializeExtension();
238                 extensionMode = true;
239                 repaint();
240             }
241             break;
242         case KeyEvent.VK_C:
243             findPreviousChangeset();
244             break;
245         case KeyEvent.VK_DELETE:
246             if (!extensionMode && dragging != null) {
247                 nodes.remove(dragging);
248                 scheduleUpdate();
249                 repaint();
250             }
251             break;
252         }
253     }
254     
255     public void keyReleased(KeyEvent e) {
256         if(e.getKeyCode() == KeyEvent.VK_CONTROL) {
257             extensionMode = false;
258             scheduleUpdate();
259             repaint();
260         }
261     }
262
263     private static Resource extractResource(Object obj) {
264         System.out.println("- " + obj.getClass().getName());
265         if(obj instanceof WorkbenchSelectionElement) {
266             Resource resource = ((WorkbenchSelectionElement)obj).getContent(new AnyResource(Simantics.getSession()));
267             if(resource != null)
268                 return resource;
269         }
270         if(obj instanceof IAdaptable) {
271             Resource resource = (Resource)((IAdaptable)obj).getAdapter(Resource.class);
272             if(resource != null)
273                 return resource;
274         }
275         return null;
276     }
277     
278     private void handleDrop(double x, double y, Object obj) {
279         //System.out.println(obj.getClass().getName());
280         if(obj instanceof IStructuredSelection) {
281             for(Object element : ((IStructuredSelection)obj).toArray()) {
282                 Resource resource = extractResource(element);
283                 if(resource != null && !nodeMap.containsKey(resource)) {
284                     addResource(x, y, resource);                    
285                     repaint();
286                 }
287             }
288         }
289     }       
290     
291     private Node addResource(double x, double y, Resource resource) {
292         Node a = new Node(new NodeData(resource));
293         a.setPos(x, y);
294         scheduleUpdate();
295         nodes.add(a);
296         return a;
297     }
298     
299     public void addResource(Resource resource) {
300         double x, y;
301         if(nodes.isEmpty()) {
302             x = 0.0;
303             y = 0.0;
304         }
305         else {
306             double xMin=Double.POSITIVE_INFINITY, yMin=Double.POSITIVE_INFINITY;
307             double xMax=Double.NEGATIVE_INFINITY, yMax=Double.NEGATIVE_INFINITY;
308             for(Node node : nodes) {
309                 xMin = Math.min(node.getMinX(), xMin);
310                 yMin = Math.min(node.getMinY(), yMin);
311                 xMax = Math.max(node.getMaxX(), xMax);
312                 yMax = Math.max(node.getMaxY(), yMax);
313             }
314             x = xMin + (xMax - xMin) * random.nextDouble();
315             y = yMin + (yMax - yMin) * random.nextDouble();
316         }
317         
318         addResource(x, y, resource);
319         repaint();
320     }
321
322     private void scheduleUpdate() {
323         Simantics.getSession().asyncRequest(new ReadRequest() {            
324             @Override
325             public void run(ReadGraph graph) throws DatabaseException {                
326                 updateNodes(graph);
327                 updateEdges(graph);
328                 SwingUtilities.invokeLater(new Runnable() {
329                     @Override
330                     public void run() {
331                         repaint();
332                     }
333                     
334                 });
335             }            
336         });        
337     }
338     
339     public void layoutGraph() {
340         ArrayList<Edge> allEdges = new ArrayList<Edge>(
341                 edges.size() + addedEdges.size() + removedEdges.size()
342                 );
343         allEdges.addAll(edges);
344         allEdges.addAll(addedEdges);
345         allEdges.addAll(removedEdges);
346         LayoutGraph.layout(
347                 nodes.toArray(new Node[nodes.size()]), 
348                 allEdges.toArray(new Edge[edges.size()]));
349         repaint();                
350     }
351     
352     private void updateNodes(ReadGraph graph) throws DatabaseException {
353         for(Node node : nodes) {
354             node.getData().updateData(graph, labelingPreferences);
355             node.setContent(new LabelContent(node.getData().getLabels()));
356         }
357         nodeMap.clear();
358         for(Node node : nodes) {
359             NodeData data = node.getData();
360             nodeMap.put(data.getResource(), node);
361         }
362     }
363     
364     private void updateEdges(ReadGraph graph) throws DatabaseException {
365         ArrayList<Edge> edges = new ArrayList<Edge>();
366         for(Node node : nodes) {
367             NodeData data = node.getData();
368             Resource subject = data.getResource();
369             ArrayList<Statement> filteredStatements = new ArrayList<Statement>(data.getStatements().size());
370             for(Statement stat : data.getStatements()) {
371                 Resource object = stat.getObject();
372                 Node node2 = nodeMap.get(object);
373                 if(node2 != null) {
374                     if(object.getResourceId() > subject.getResourceId() ||
375                             graph.getPossibleInverse(stat.getPredicate()) == null) {
376                         edges.add(createEdge(graph, stat, node, node2));
377                     }
378                 }
379                 else
380                     filteredStatements.add(stat);
381             }
382             data.setStatements(filteredStatements);
383         }
384         this.edges = edges;
385         this.addedEdges = filterEdgesWithoutNodes( this.addedEdges );
386         this.removedEdges = filterEdgesWithoutNodes( this.removedEdges );
387     }
388
389     private ArrayList<Edge> filterEdgesWithoutNodes(Collection<Edge> edges) {
390         ArrayList<Edge> result = new ArrayList<Edge>(edges.size());
391         for (Edge e : edges) {
392             if (!nodeMap.containsValue(e.getA()) || !nodeMap.containsValue(e.getB()))
393                 continue;
394             result.add(e);
395         }
396         return result;
397     }
398
399     private Edge createEdge(ReadGraph graph, Statement stat, Node n1, Node n2) throws DatabaseException {
400         Resource predicate = stat.getPredicate();
401         Resource inverse = graph.getPossibleInverse(predicate);        
402         if(inverse != null) {
403             Layer0 L0 = Layer0.getInstance(graph);
404             if(graph.hasStatement(predicate, L0.PartOf, inverse)
405                     || predicate.equals(L0.PartOf)
406                     || predicate.equals(L0.SuperrelationOf)
407                     || predicate.equals(L0.SupertypeOf)) {
408                 predicate = inverse;
409                 Node temp = n1;
410                 n1 = n2;
411                 n2 = temp;
412             }
413         }
414         Edge edge = new Edge(n1, n2);
415         edge.setContent(new LabelContent(new String[] {
416                 NameUtils.getSafeName(graph, predicate)}));
417         return edge;
418     }
419     
420     Node dragging = null;
421     double dragDX, dragDY;
422     private void handleMouseLeftPressed(double x, double y) {
423         Node node;
424         if(extensionMode) {
425             node = pickExtension(x, y);
426             if(node != null) {
427                 nodes.add(node);
428                 extensionNodes.remove(node);
429             }
430         }
431         else
432             node = pick(x, y);
433         if(node != null) {
434             dragDX = x - node.getX();
435             dragDY = y - node.getY();
436             dragging = node;
437         }
438     }
439     
440     Point2D panningStartMouse;
441     private void handleMouseMiddlePressed(double x, double y) {
442         panningStartMouse = new Point2D.Double(x, y);
443     }
444     
445     private void handleMouseMoved(double x, double y) {
446         if(dragging != null) {
447             dragging.setPos(x-dragDX, y-dragDY);
448             repaint();
449         }
450         if(panningStartMouse != null) {
451             canvasPosX -= x - panningStartMouse.getX();
452             canvasPosY -= y - panningStartMouse.getY();
453             repaint();
454         }
455     }
456     
457     private void handleMouseWheelMoved(double x, double y, double amount) {
458         double s = Math.exp(-0.2*amount);
459         canvasZoom *= s;
460         canvasPosX = x - (x-canvasPosX)/s;
461         canvasPosY = y - (y-canvasPosY)/s;
462         repaint();
463     }
464
465     private void handleMouseReleased(double x, double y) {
466         dragging = null;
467         panningStartMouse = null;
468     }
469     
470     public void zoomToFit() {
471         if(!nodes.isEmpty()) {
472             double minX = Double.POSITIVE_INFINITY;
473             double minY = Double.POSITIVE_INFINITY;
474             double maxX = Double.NEGATIVE_INFINITY;
475             double maxY = Double.NEGATIVE_INFINITY;
476             System.out.println("(" + minX + "," + minY + ") - (" + maxX + "," + maxY + ")");
477             for(Node node : nodes) {
478                 minX = Math.min(minX, node.getMinX());
479                 minY = Math.min(minY, node.getMinY());
480                 maxX = Math.max(maxX, node.getMaxX());
481                 maxY = Math.max(maxY, node.getMaxY());
482             }            
483             canvasZoom = Math.min(getWidth()/(maxX-minX), getHeight()/(maxY-minY));
484             canvasZoom *= 0.9;
485             canvasPosX = minX - 0.5 * (getWidth()/canvasZoom - maxX+minX);
486             canvasPosY = minY - 0.5 * (getHeight()/canvasZoom - maxY+minY);
487             repaint();
488         }
489     }
490
491     THashMap<Resource, Node> extensionNodeMap = new THashMap<Resource, Node>();
492     public void initializeExtension() {
493         extensionNodes.clear();
494         extensionEdges.clear();
495         try {
496             Simantics.getSession().syncRequest(new ReadRequest() {
497                 @Override
498                 public void run(ReadGraph graph) throws DatabaseException {
499                     SCLContext sclContext = SCLContext.getCurrent();
500                     Object oldGraph = sclContext.put("graph", graph);
501                     try {
502                         THashMap<Resource, Node> oldExtensionNodeMap = DebuggerCanvas.this.extensionNodeMap;
503                         THashMap<Resource, Node> extensionNodeMap = new THashMap<Resource, Node>();
504                         for(Node node : nodes) {
505                             for(Statement stat : node.getData().getStatements()) {
506                                 Resource object = stat.getObject();
507                                 Node node2 = extensionNodeMap.get(object);
508                                 if(node2 == null) {
509                                     if(statementFilter != null && Boolean.FALSE.equals(statementFilter.apply(stat)))
510                                         continue;
511                                     node2 = oldExtensionNodeMap.get(object);
512                                     if(node2 == null) {
513                                         node2 = new Node(new NodeData(object));
514                                         double angle = random.nextDouble() * Math.PI * 2.0;
515                                         double dx = Math.cos(angle);
516                                         double dy = Math.sin(angle);
517                                         double len = 150.0;
518                                         node2.setPos(node.getX() + dx*len, node.getY() + dy*len);
519                                     }
520                                     node2.getData().updateData(graph, labelingPreferences);                                
521                                     node2.setContent(new LabelContent(node2.getData().getLabels()));
522                                     extensionNodeMap.put(object, node2);
523                                     extensionNodes.add(node2);
524                                 }
525                                 extensionEdges.add(createEdge(graph, stat, node, node2));
526                             }
527                         }
528                     } catch (Throwable t) {
529                         if (t instanceof DatabaseException)
530                             throw (DatabaseException) t;
531                         throw new DatabaseException(t);
532                     } finally {
533                         sclContext.put("graph", oldGraph);
534                     }
535                     DebuggerCanvas.this.extensionNodeMap = extensionNodeMap;
536                     layoutExtension();
537                 }      
538             });
539         } catch (DatabaseException e) {
540             e.printStackTrace();
541         }
542     }
543
544     private void layoutExtension() {        
545         TObjectIntHashMap<Node> extensionNodeIds = new TObjectIntHashMap<Node>();
546         for(int i=0;i<extensionNodes.size();++i)
547             extensionNodeIds.put(extensionNodes.get(i), i);
548         
549         double[][] neighbors = new double[extensionNodes.size()][];
550         {
551             TDoubleArrayList[] neighborLists = new TDoubleArrayList[neighbors.length];        
552             for(int i=0;i<neighborLists.length;++i)
553                 neighborLists[i] = new TDoubleArrayList();
554             for(Edge edge : extensionEdges) {
555                 int id;
556                 Node node;
557                 if(extensionNodeIds.containsKey(edge.getA())) {
558                     id = extensionNodeIds.get(edge.getA());
559                     node = edge.getB();
560                 }
561                 else {
562                     id = extensionNodeIds.get(edge.getB());
563                     node = edge.getA();
564                 }
565                 TDoubleArrayList list = neighborLists[id];
566                 list.add(node.getX());
567                 list.add(node.getY());
568             }            
569             for(int i=0;i<neighborLists.length;++i) {
570                 neighbors[i] = neighborLists[i].toArray();
571             }
572         }
573         
574         double[] fixedRepulsiveX = new double[nodes.size()];
575         double[] fixedRepulsiveY = new double[nodes.size()];
576         for(int i=0;i<nodes.size();++i) {
577             Node node = nodes.get(i);
578             fixedRepulsiveX[i] = node.getX();
579             fixedRepulsiveY[i] = node.getY();
580         }
581         ExtensionLayoutAlgorithm algo = 
582                 new ExtensionLayoutAlgorithm(neighbors, fixedRepulsiveX, fixedRepulsiveY);
583         double[] posX = algo.getPosX();
584         double[] posY = algo.getPosY();
585         for(int i=0;i<extensionNodes.size();++i) {
586             posX[i] = extensionNodes.get(i).getX();
587             posY[i] = extensionNodes.get(i).getY();
588         }        
589         algo.optimize();
590         for(int i=0;i<extensionNodes.size();++i) {
591             extensionNodes.get(i).setPos(posX[i], posY[i]);
592         }        
593     }      
594     
595     private Node getNode(Resource resource) {
596         Node node = nodeMap.get(resource);
597         if(node == null) {
598             node = addResource(random.nextDouble()*200.0-100.0, 
599                     random.nextDouble()*200.0-100.0, 
600                     resource);
601             nodeMap.put(resource, node);
602         }
603         return node;
604     }
605     
606     public void findPreviousChangeset() {
607         try {
608             Session session = Simantics.getSession();
609             final ManagementSupport ms = session.getService(ManagementSupport.class);
610             final long lastId = ms.getHeadRevisionId();
611             final long firstId = getOpId(ms, lastId);
612             //System.out.println(firstId + "-" + lastId);
613             addedEdges.clear();
614             removedEdges.clear();            
615             session.asyncRequest(new ReadRequest() {
616                 @Override
617                 public void run(ReadGraph graph) throws DatabaseException {
618                     final Collection<ChangeSet> css = 
619                             ms.fetchChangeSets(graph, firstId, lastId);
620                     Layer0 L0 = Layer0.getInstance(graph);
621                     for(ChangeSet cs : css) {
622                         for(ChangeSet.StatementChange stat : cs.changedStatements()) {
623                             Resource predicate = stat.getPredicate();
624                             if(predicate.equals(L0.InstanceOf) ||
625                                     predicate.equals(L0.HasName) ||
626                                     predicate.equals(L0.NameOf))
627                                 continue;
628                             Edge edge = createEdge(graph, stat, 
629                                     getNode(stat.getSubject()),
630                                     getNode(stat.getObject()));
631                             if(stat.isClaim())
632                                 addedEdges.add(edge);
633                             else
634                                 removedEdges.add(edge);
635                         }
636                     }    
637                     scheduleUpdate();
638                 }                
639             });                  
640         } catch(DatabaseException e) {
641             e.printStackTrace();
642         }
643     } 
644     
645     private static long getOpId(ManagementSupport ms, long revisionId) throws DatabaseException {
646         Collection<ChangeSetIdentifier> ids = ms.getChangeSetIdentifiers(revisionId, revisionId);
647         ChangeSetIdentifier curId = ids.iterator().next();
648         byte[] opIdData = curId.getMetadata().get("opid");
649         System.out.println(new String(opIdData));
650         long opId = Long.parseLong(new String(opIdData));
651         if(opId == 0)
652             opId = revisionId;
653         return opId;
654     }
655
656 }