1 package org.simantics.debug.ui.graph;
\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
19 import javax.swing.SwingUtilities;
\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
51 public class GraphicalDebugger extends GraphDebugger {
\r
53 public enum Style{dot,neato,fdp,sfdp,twopi,circo};
\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
61 private GraphvizComponent2 graphVizComponent;
\r
63 private int depth = 1;
\r
64 private Style style = Style.dot;
\r
66 public GraphicalDebugger(Composite parent, int style, final Session session, Resource resource) {
\r
67 super(parent, style, session, resource);
\r
70 public void defaultInitializeUI() {
\r
71 setLayout(new GridLayout(2, false));
\r
72 setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
\r
74 createResourceText(this);
\r
75 createDropLabel(this);
\r
80 public int getDepth() {
\r
84 public void setDepth(int depth) {
\r
87 if(depth == this.depth)
\r
93 public Style getGraphStyle() {
\r
97 public void setGraphStyle(Style style) {
\r
98 if (this.style == style)
\r
100 this.style = style;
\r
105 protected void initializeCSS() {
\r
110 public Browser createBrowser(Composite parent) {
\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
122 public void run() {
\r
123 graphVizComponent.getCanvas().addMouseListener(new MouseListener() {
\r
126 public void mouseReleased(MouseEvent arg0) {
\r
131 public void mousePressed(MouseEvent arg0) {
\r
136 public void mouseExited(MouseEvent arg0) {
\r
141 public void mouseEntered(MouseEvent arg0) {
\r
146 public void mouseClicked(MouseEvent arg0) {
\r
147 if (arg0.getClickCount() > 1) {
\r
148 Point p = arg0.getPoint();
\r
156 GridDataFactory.fillDefaults().span(2, 1).grab(true, true).applyTo(graphVizComponent);
\r
158 return graphVizComponent;
\r
161 protected void pick(Point p) {
\r
163 AffineTransform at = ((ViewerCanvas)graphVizComponent.getCanvas()).getTransform();
\r
164 Point2D pickPoint = new Point2D.Double();
\r
166 at.inverseTransform(new Point2D.Double((double)p.x,(double)p.y), pickPoint);
\r
167 } catch (NoninvertibleTransformException e) {
\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
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
193 protected Node getOrCreate(Resource r) {
\r
194 Node n = nodeMap.getRight(r);
\r
196 n = new Node(graph);
\r
197 if (!r.isPersistent()) {
\r
199 n.setFontColor("blue");
\r
206 @SuppressWarnings("unused")
\r
207 protected void appendLabel(Node node, String text) {
\r
208 String label = node.get("label");
\r
210 if (label == null || label.length() == 0)
\r
211 label = text;//escape(text);
\r
213 label = label.substring(1,label.length()-1);
\r
214 label += "<br/>"+text;
\r
216 label = "<" + label + ">";
\r
218 if (label == null || label.length() == 0)
\r
225 node.setLabel(label);
\r
231 protected synchronized void updateContent(final ReadGraph g, Resource... resources) throws DatabaseException {
\r
232 L0 = Layer0.getInstance(g);
\r
234 graph = new Graph();
\r
235 graph.setRankdir("LR");
\r
243 //StringBuffer content = new StringBuffer();
\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
250 // content.append("</div>\n");
\r
251 //content.append("</body></html>\n");
\r
252 createContent(g, depth, resources);
\r
255 //graph.write(System.out);
\r
256 graphVizComponent.setGraph(graph,style.toString());
\r
259 private void createContent(final ReadGraph g, int iter, Resource... resources) throws DatabaseException {
\r
262 for (Resource r : resources) {
\r
265 if (processed.contains(r))
\r
267 Node node = getResourceRef(g, r);
\r
268 if (r.equals(getDebuggerLocation())) {
\r
269 node.setFillColor("#aaffaa");
\r
270 node.setStyle("filled");
\r
272 //Node node = getOrCreate(r);
\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
285 appendLabel(node, "URI: " + uri);
\r
286 //content.append("\t\t<div class=\"monospaced\">" + uri + "</div><br/>");
\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
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
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
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
312 boolean isOrderedSet = g.isInstanceOf(r, L0.OrderedSet);
\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
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
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
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
346 // FIXME : should we inform missing inverse
\r
351 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Is Related To</td></tr>");
\r
353 // ELEMENTS OF ORDERED SET
\r
355 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered set</td></tr>");
\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
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
370 strmap.put(pred, str);
\r
372 Arrays.sort(preds, new Comparator<Resource>() {
\r
374 public int compare(Resource o1, Resource o2) {
\r
375 return strmap.get(o1).compareTo(strmap.get(o2));
\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
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
389 Resource objects[] = new Resource[statements.size()];
\r
391 for (Statement stm : statements) {
\r
392 objects[i] = stm.getObject();
\r
395 createContent(g, iter-1, objects);
\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
408 private class NodeObject {
\r
409 public String name;
\r
411 @SuppressWarnings("unused")
\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
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
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
434 objects[i].node.setFillColor("#ffaaaa");
\r
435 objects[i].node.setStyle("filled");
\r
439 // Sort statements by object name
\r
440 Arrays.sort(objects, new Comparator<NodeObject>() {
\r
442 public int compare(NodeObject o1, NodeObject o2) {
\r
443 return o1.name.compareTo(o2.name);
\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
457 //content.append("<td rowspan=\"" + objects.length + "\" valign=\"top\">" + getResourceRef(graph, pred) + "</td>");
\r
460 //if (objects[i][3] == null) content.append("<td>");
\r
461 //else content.append("<td class=\"acquired\">");
\r
463 //content.append(objects[i][2]);
\r
464 //if (objects[i][3] != null)
\r
465 // content.append(objects[i][3]);
\r
467 //content.append("</td>");
\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
476 //content.append("</tr>");
\r
480 private void updateTag(Node node, ReadGraph graph, Resource subj, Resource tag, String title) throws DatabaseException {
\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
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
498 private void updateOrderedSet(Node node, ReadGraph graph, Resource subj) throws DatabaseException {
\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
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
517 // else if(stat.getPredicate().equals(inv)) {
\r
518 // list.add("prev " + getResourceRef(graph, stat.getObject()));
\r
524 if(cur.equals(subj))
\r
526 //list.add(getResourceName(graph, cur));
\r
527 list.add(getResourceRef(graph, cur));
\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
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
540 // content.append("<td rowspan=\"" + list.size() + "\" valign=\"top\">Ordered Set Elements</td>");
\r
542 // // Object column
\r
543 // content.append("<td>");
\r
544 // content.append(list.get(i));
\r
545 // content.append("</td>");
\r
547 // content.append("</tr>");
\r
549 // content.append("</table>\n");
\r
550 // appendLabel(node, content.toString());
\r