]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.debug.ui/src/org/simantics/debug/ui/graph/GraphicalDebugger.java
fef011b8d4efdcc754d314d23f8df5003ce53b59
[simantics/platform.git] / bundles / org.simantics.debug.ui / src / org / simantics / debug / ui / graph / GraphicalDebugger.java
1 package org.simantics.debug.ui.graph;
2
3 import java.awt.Point;
4 import java.awt.event.MouseEvent;
5 import java.awt.event.MouseListener;
6 import java.awt.geom.AffineTransform;
7 import java.awt.geom.NoninvertibleTransformException;
8 import java.awt.geom.Point2D;
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.Collection;
12 import java.util.Comparator;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import javax.swing.SwingUtilities;
20
21 import org.eclipse.jface.layout.GridDataFactory;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.browser.Browser;
24 import org.eclipse.swt.layout.GridData;
25 import org.eclipse.swt.layout.GridLayout;
26 import org.eclipse.swt.widgets.Composite;
27 import org.simantics.db.ReadGraph;
28 import org.simantics.db.Resource;
29 import org.simantics.db.Session;
30 import org.simantics.db.Statement;
31 import org.simantics.db.common.uri.ResourceToPossibleURI;
32 import org.simantics.db.common.utils.OrderedSetUtils;
33 import org.simantics.db.exception.DatabaseException;
34 import org.simantics.db.exception.ValidationException;
35 import org.simantics.db.service.ClusteringSupport;
36 import org.simantics.debug.ui.GraphDebugger;
37 import org.simantics.debug.ui.internal.HashMultiMap;
38 import org.simantics.graphviz.Edge;
39 import org.simantics.graphviz.Graph;
40 import org.simantics.graphviz.IGraphPart;
41 import org.simantics.graphviz.Node;
42 import org.simantics.graphviz.drawable.ViewerCanvas;
43 import org.simantics.graphviz.ui.GraphvizComponent2;
44 import org.simantics.layer0.Layer0;
45 import org.simantics.utils.datastructures.BijectionMap;
46 import org.simantics.utils.datastructures.MapList;
47 import org.simantics.utils.ui.ErrorLogger;
48
49
50
51 public class GraphicalDebugger extends GraphDebugger {
52         
53         public enum Style{dot,neato,fdp,sfdp,twopi,circo};
54         
55         private Graph graph;
56         private BijectionMap<Resource, Node> nodeMap = new BijectionMap<Resource, Node>();
57         private MapList<Resource, Edge> edgeMap = new MapList<Resource, Edge>();
58         private Map<Edge,Resource> edgeMap2 = new HashMap<Edge, Resource>();
59         private Set<Resource> processed = new HashSet<Resource>();
60         
61         private GraphvizComponent2 graphVizComponent;
62         
63         private int depth = 1;
64         private Style style = Style.dot;
65         
66         public GraphicalDebugger(Composite parent, int style, final Session session, Resource resource) {
67                 super(parent, style, session, resource);
68         }
69         
70         public void defaultInitializeUI() {
71         setLayout(new GridLayout(2, false));
72         setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
73
74         createResourceText(this);
75         createDropLabel(this);
76         createGraph(this);
77
78     }
79         
80         public int getDepth() {
81                 return depth;
82         }
83         
84         public void setDepth(int depth) {
85                 if (depth < 1)
86                         return;
87                 if(depth == this.depth)
88                         return;
89                 this.depth = depth;
90                 refreshBrowser();
91         }
92         
93         public Style getGraphStyle() {
94                 return style;
95         }
96         
97         public void setGraphStyle(Style style) {
98                 if (this.style == style)
99                         return;
100                 this.style = style;
101                 refreshBrowser();
102         }
103         
104         @Override
105         protected void initializeCSS() {
106                 // do nothing
107         }
108         
109         @Override
110         public Browser createBrowser(Composite parent) {
111                 // do nothing
112                 return null;
113         }
114         
115         public GraphvizComponent2 createGraph(Composite parent) {
116                 graph = new Graph();
117                 graph.setRankdir("LR");
118                 graphVizComponent = new GraphvizComponent2(parent, SWT.NONE);
119                 SwingUtilities.invokeLater(new Runnable() {
120                         
121                         @Override
122                         public void run() {
123                                 graphVizComponent.getCanvas().addMouseListener(new MouseListener() {
124                                         
125                                         @Override
126                                         public void mouseReleased(MouseEvent arg0) {
127
128                                         }
129                                         
130                                         @Override
131                                         public void mousePressed(MouseEvent arg0) {
132
133                                         }
134                                         
135                                         @Override
136                                         public void mouseExited(MouseEvent arg0) {
137
138                                         }
139                                         
140                                         @Override
141                                         public void mouseEntered(MouseEvent arg0) {
142
143                                         }
144                                         
145                                         @Override
146                                         public void mouseClicked(MouseEvent arg0) {
147                                                 if (arg0.getClickCount() > 1) {
148                                                         Point p = arg0.getPoint();
149                                                         pick(p);
150                                                 }
151                                         }
152                                 });
153                         }
154                 });
155
156                 GridDataFactory.fillDefaults().span(2, 1).grab(true, true).applyTo(graphVizComponent);
157                 refreshBrowser();
158                 return graphVizComponent;
159         }
160         
161         protected void pick(Point p) {
162                 
163                 AffineTransform at = ((ViewerCanvas)graphVizComponent.getCanvas()).getTransform();
164                 Point2D pickPoint = new Point2D.Double();
165                 try {
166                         at.inverseTransform(new Point2D.Double((double)p.x,(double)p.y), pickPoint);
167                 } catch (NoninvertibleTransformException e) {
168                         return;
169                 }
170
171                 Collection<IGraphPart> parts = graphVizComponent.getDrawable().pick(pickPoint);
172                 for (IGraphPart part : parts) {
173                         if (part instanceof Node) {
174                                 Resource r = nodeMap.getLeft((Node)part);
175                                 if (r != null && !r.equals(getDebuggerLocation())) {
176                                         changeLocation(r);
177                                         return;
178                                 } 
179                                 
180                         } 
181                 }
182                 for (IGraphPart part : parts) {
183                         if (part instanceof Edge) {
184                                 Resource r = edgeMap2.get(part);
185                                 if (r != null && !r.equals(getDebuggerLocation())) {
186                                         changeLocation(r);
187                                         return;
188                                 }
189                         }
190                 }
191         }
192         
193         protected Node getOrCreate(Resource r) {
194                 Node n = nodeMap.getRight(r);
195                 if (n == null) {
196                         n = new Node(graph);
197                         if (!r.isPersistent()) {
198                                 n.setShape("box");
199                                 n.setFontColor("blue");
200                         }
201                         nodeMap.map(r, n);
202                 }
203                 return n;
204         }
205         
206         @SuppressWarnings("unused")
207         protected void appendLabel(Node node, String text) {
208                 String label = node.get("label");
209                 if (true) {
210                         if (label == null || label.length() == 0)
211                                 label = text;//escape(text);
212                         else {
213                                 label = label.substring(1,label.length()-1);
214                                 label += "<br/>"+text;
215                         }
216                         label = "<" + label + ">";
217                 } else {
218                         if (label == null || label.length() == 0)
219                                 label = text;
220                         else {
221                                 label += " "+text;
222                         }
223                         
224                 }
225                 node.setLabel(label);
226         }
227         
228
229
230         
231         protected synchronized void updateContent(final ReadGraph g, Resource... resources) throws DatabaseException {
232                 L0 = Layer0.getInstance(g);
233                 
234                 graph = new Graph();
235                 graph.setRankdir("LR");
236                 
237                 nodeMap.clear();
238                 edgeMap.clear();
239                 edgeMap2.clear();
240                 processed.clear();
241                 
242                 //links.clear();
243         //StringBuffer content = new StringBuffer();
244
245         // Generate HTML -page
246 //        content.append("<html><head>" + getHead() + "</head>\n");
247 //        content.append("<body>\n");
248 //        content.append("<div id=\"mainContent\">\n");
249
250        // content.append("</div>\n");
251         //content.append("</body></html>\n");
252                 createContent(g, depth, resources);
253        
254                 // Update content
255         //graph.write(System.out);
256         graphVizComponent.setGraph(graph,style.toString());
257         }
258         
259         private void createContent(final ReadGraph g, int iter, Resource... resources) throws DatabaseException {
260                 if (iter == 0)
261                         return;
262         for (Resource r : resources) {
263             if (r == null)
264                 continue;
265             if (processed.contains(r))
266                 continue;
267             Node node = getResourceRef(g, r);
268             if (r.equals(getDebuggerLocation())) {
269                 node.setFillColor("#aaffaa");
270                                 node.setStyle("filled");
271             }
272           //Node node = getOrCreate(r);
273             processed.add(r);
274             
275             
276             
277             String uri = null;
278             try {
279                 uri = g.syncRequest(new ResourceToPossibleURI(r));
280             } catch (Exception e) {
281                 e.printStackTrace();
282                 uri = "Cannot get URI: " + e.getMessage();
283             }
284             if (uri != null)
285                 appendLabel(node, "URI: " + uri);
286                 //content.append("\t\t<div class=\"monospaced\">" + uri + "</div><br/>");
287
288             Collection<Statement> statements = g.getStatements(r, L0.IsWeaklyRelatedTo);
289             HashMultiMap<Resource, Resource[]> map = new HashMultiMap<Resource, Resource[]>();
290             for(org.simantics.db.Statement statement : statements) {
291                 Resource predicate = null;
292                 Resource subject = null;
293                 Resource obj = null;
294                 try {
295                     predicate = statement.getPredicate();
296                     subject = statement.getSubject();
297                     obj = statement.getObject();
298                     map.add(predicate, new Resource[] {subject, obj});
299                 } catch (Throwable e) {
300                     e.printStackTrace();
301                     ErrorLogger.defaultLogError("Cannot find statement " + subject + " " + predicate + " " + obj, e);
302                 }
303             }
304             ClusteringSupport support = g.getSession().getService(ClusteringSupport.class);
305             //content.append("<h3>" + " ["+ r.getResourceId() + "-" + support.getCluster(r) + "] " + "</h3>\n");
306             appendLabel(node,  " ["+ r.getResourceId() + "-" + support.getCluster(r) + "]");
307             
308             //content.append("<table>\n");
309             //content.append("<tr><th>Predicate</th><th>Object</th></tr>");
310             //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Basic information</td></tr>");
311
312             boolean isOrderedSet = g.isInstanceOf(r, L0.OrderedSet);
313             map.remove(r);
314
315             // BASIC INFORMATION:
316             for (Resource pred :
317                 new Resource[] {L0.HasName, L0.InstanceOf,
318                     L0.Inherits, L0.SubrelationOf,
319                     L0.ConsistsOf, L0.PartOf})
320                 if (map.containsKey(pred))
321                     updatePred(node, g, r, pred, map.remove(pred));
322
323             // TAGS
324             //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Tags</td></tr>");
325             for(Statement stm : statements) {
326                 if(stm.getSubject().equals(stm.getObject())) {
327                     updateTag(node, g, r, stm.getPredicate(), "Tag");
328                     map.remove(stm.getPredicate());
329                 }
330             }
331
332             // ORDERED SETS
333             //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered Sets</td></tr>");
334             for(Statement stm : statements) {
335                 Resource predicate = stm.getPredicate();
336                 if(g.isInstanceOf(stm.getPredicate(), L0.OrderedSet)) {
337                     updateTag(node, g, r, stm.getPredicate(), "Ordered Set");
338                     map.remove(stm.getPredicate());
339                 }
340                 Resource inverse = g.getPossibleInverse(predicate);
341                 if (inverse != null) {
342                     if(g.isInstanceOf(inverse, L0.OrderedSet)) {
343                         map.remove(stm.getPredicate());
344                     }
345                 } else {
346                     // FIXME : should we inform missing inverse
347                 }
348             }
349
350             // IS RELATED TO
351             //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Is Related To</td></tr>");
352
353             // ELEMENTS OF ORDERED SET
354             if(isOrderedSet) {
355                 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered set</td></tr>");
356                 try {
357                     updateOrderedSet(node, g, r);
358                 } catch (ValidationException e) {
359                     //content.append("<td colspan=\"2\"><span style=\"color:red;font-weight:bold\">BROKEN ORDERED SET:<br/></span><span style=\"color:red\">" + e.getMessage() + "</span></td>");
360                 }
361             }
362
363             // IS RELATED TO (other)
364             Resource[] preds = map.keySet().toArray(new Resource[0]);
365             final Map<Resource, String> strmap = new HashMap<Resource, String>(preds.length);
366             for(Resource pred : preds) {
367                 String str = htmlEscape(getResourceName(g, pred));
368                 if(str == null)
369                     str = "<null>";
370                 strmap.put(pred, str);
371             }
372             Arrays.sort(preds, new Comparator<Resource>() {
373                 @Override
374                 public int compare(Resource o1, Resource o2) {
375                     return strmap.get(o1).compareTo(strmap.get(o2));
376                 }
377             });
378             for(Resource pred : preds)
379                 if(g.isSubrelationOf(pred, L0.IsRelatedTo))
380                     updatePred(node, g, r, pred, map.get(pred));
381
382             // OTHER STATEMENTS
383             //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Other statements</td></tr>");
384             for(Resource pred : preds)
385                 if(!g.isSubrelationOf(pred, L0.IsRelatedTo))
386                     updatePred(node, g, r, pred, map.get(pred));
387            // content.append("</table>\n");
388             
389             Resource objects[] = new Resource[statements.size()];
390             int i = 0;
391             for (Statement stm : statements) {
392                 objects[i] = stm.getObject();
393                 i++;
394             }
395             createContent(g, iter-1, objects);
396         }
397         }
398         
399         private Node getResourceRef(ReadGraph graph, Resource r) throws DatabaseException {
400         Node node = nodeMap.getRight(r);
401         if (node == null) {
402                 node = getOrCreate(r);
403                 appendLabel(node, htmlEscape(getResourceName(graph, r)));
404         }
405         return node;
406     }
407         
408         private class NodeObject {
409                 public String name;
410                 public Node node;
411                 @SuppressWarnings("unused")
412                 public Resource r;
413                 
414         }
415         
416         private void updatePred(Node node, ReadGraph graph, Resource subj, Resource pred, List<Resource[]> stats) throws DatabaseException {
417         // Generate output content from statements
418         NodeObject[] objects = new NodeObject[stats.size()];
419         for (int i = 0; i < stats.size(); ++i) {
420             Resource stmSubject = stats.get(i)[0];
421             Resource object = stats.get(i)[1];
422
423             objects[i] = new NodeObject();
424             objects[i].r = object;
425             objects[i].name = htmlEscape( getResourceName(graph, object) );
426             objects[i].node = getResourceRef(graph, object);
427
428             // Make a note if the statement was acquired.
429             if(!stmSubject.equals(subj)) {
430                 Node asserted = getResourceRef(graph, stmSubject);
431                 Edge e = new Edge(this.graph, objects[i].node, asserted);
432                 e.setLabel("Asserted in");
433                 
434                 objects[i].node.setFillColor("#ffaaaa");
435                 objects[i].node.setStyle("filled");
436             }
437         }
438
439         // Sort statements by object name
440         Arrays.sort(objects, new Comparator<NodeObject>() {
441             @Override
442             public int compare(NodeObject o1, NodeObject o2) {
443                 return o1.name.compareTo(o2.name);
444             }
445         });
446
447         String predName = getResourceName(graph, pred);
448         // Output table rows
449         for (int i = 0; i < objects.length; ++i) {
450                 Edge e = new Edge(this.graph,node, objects[i].node);
451                 e.setLabel(predName);
452                 edgeMap.add(pred, e);
453                 edgeMap2.put(e, pred);
454             //content.append("<tr>");
455             // Predicate column
456             //if (i == 0)
457                 //content.append("<td rowspan=\"" + objects.length + "\" valign=\"top\">" + getResourceRef(graph, pred) + "</td>");
458
459             // Object column
460             //if (objects[i][3] == null) content.append("<td>");
461             //else content.append("<td class=\"acquired\">");
462
463             //content.append(objects[i][2]);
464             //if (objects[i][3] != null)
465             //    content.append(objects[i][3]);
466
467             //content.append("</td>");
468
469             // Statement remove -link column
470             // Only allowed for non-acquired statements.
471             //if (objects[i][3] == null) {
472             //    content.append("<td class=\"remove\">");
473             //    content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0])));
474             //    content.append("</td>");
475             //}
476             //content.append("</tr>");
477         }
478     }
479         
480         private void updateTag(Node node, ReadGraph graph, Resource subj, Resource tag, String title) throws DatabaseException {
481
482         // Generate output content from statements
483         Node ref = getResourceRef(graph, tag);
484         Edge e = new Edge(this.graph, node, ref);
485         e.setLabel(title);
486         
487 //        content.append("<tr>");
488 //        content.append("<td rowspan=\"1\" colspan=\"2\" valign=\"top\">" + ref + "</td>");
489 //        content.append("<td class=\"remove\">");
490 //        content.append(getStatementRemoveRef(subj, tag, subj));
491 //        content.append("</td>");
492 //        content.append("</tr>");
493
494     }
495         
496         
497         
498         private void updateOrderedSet(Node node, ReadGraph graph, Resource subj) throws DatabaseException {
499
500 //              StringBuffer content = new StringBuffer();
501         //List<String> list = new ArrayList<String>();
502                 List<Node> list = new ArrayList<Node>();
503         Resource cur = subj;
504         while(true) {
505             try {
506                 cur = OrderedSetUtils.next(graph, subj, cur);
507             } catch(DatabaseException e) {
508                 Edge edge = new Edge(this.graph, node, node);
509                 edge.setLabel("Broken Ordered Set");
510                 //list.add("<span style=\"color:red;font-weight:bold\">BROKEN ORDERED SET:<br/></span><span style=\"color:red\">" + e.getMessage() + "</span>");
511 //                Resource inv = graph.getPossibleInverse(subj);
512 //                for(Statement stat : graph.getStatements(cur, L0.IsRelatedTo)) {
513 //                    if(stat.getSubject().equals(cur)) {
514 //                        if(stat.getPredicate().equals(subj)) {
515 //                            list.add("next " + getResourceRef(graph, stat.getObject()));
516 //                        }
517 //                        else if(stat.getPredicate().equals(inv)) {
518 //                            list.add("prev " + getResourceRef(graph, stat.getObject()));
519 //                        }
520 //                    }
521 //                }
522 //                break;
523             }
524             if(cur.equals(subj))
525                 break;
526             //list.add(getResourceName(graph, cur));
527             list.add(getResourceRef(graph, cur));
528         }
529         for (int i = 0; i < list.size() ; ++i) {
530                  Edge e = new Edge(this.graph, node, list.get(i));
531                  e.setLabel("Oredered set item " + i);
532         }
533
534         // Output table rows
535 //        content.append("<table>\n");
536 //        for (int i = 0; i < list.size() ; ++i) {
537 //            content.append("<tr>");
538 //            // Predicate column
539 //            if (i == 0)
540 //                content.append("<td rowspan=\"" + list.size() + "\" valign=\"top\">Ordered Set Elements</td>");
541 //
542 //            // Object column
543 //            content.append("<td>");
544 //            content.append(list.get(i));
545 //            content.append("</td>");
546 //
547 //            content.append("</tr>");
548 //        }
549 //        content.append("</table>\n");
550 //        appendLabel(node, content.toString());
551     }
552         
553         
554         
555 }