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