1 package org.simantics.debug.ui.graph;
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;
19 import javax.swing.SwingUtilities;
21 import org.eclipse.jface.layout.GridDataFactory;
22 import org.eclipse.osgi.util.NLS;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.browser.Browser;
25 import org.eclipse.swt.layout.GridData;
26 import org.eclipse.swt.layout.GridLayout;
27 import org.eclipse.swt.widgets.Composite;
28 import org.simantics.db.ReadGraph;
29 import org.simantics.db.Resource;
30 import org.simantics.db.Session;
31 import org.simantics.db.Statement;
32 import org.simantics.db.common.uri.ResourceToPossibleURI;
33 import org.simantics.db.common.utils.OrderedSetUtils;
34 import org.simantics.db.exception.DatabaseException;
35 import org.simantics.db.exception.ValidationException;
36 import org.simantics.db.service.ClusteringSupport;
37 import org.simantics.debug.ui.GraphDebugger;
38 import org.simantics.debug.ui.internal.HashMultiMap;
39 import org.simantics.graphviz.Edge;
40 import org.simantics.graphviz.Graph;
41 import org.simantics.graphviz.IGraphPart;
42 import org.simantics.graphviz.Node;
43 import org.simantics.graphviz.drawable.ViewerCanvas;
44 import org.simantics.graphviz.ui.GraphvizComponent2;
45 import org.simantics.layer0.Layer0;
46 import org.simantics.utils.datastructures.BijectionMap;
47 import org.simantics.utils.datastructures.MapList;
48 import org.simantics.utils.ui.ErrorLogger;
52 public class GraphicalDebugger extends GraphDebugger {
54 public enum Style{dot,neato,fdp,sfdp,twopi,circo};
57 private BijectionMap<Resource, Node> nodeMap = new BijectionMap<Resource, Node>();
58 private MapList<Resource, Edge> edgeMap = new MapList<Resource, Edge>();
59 private Map<Edge,Resource> edgeMap2 = new HashMap<Edge, Resource>();
60 private Set<Resource> processed = new HashSet<Resource>();
62 private GraphvizComponent2 graphVizComponent;
64 private int depth = 1;
65 private Style style = Style.dot;
67 public GraphicalDebugger(Composite parent, int style, final Session session, Resource resource) {
68 super(parent, style, session, resource);
71 public void defaultInitializeUI() {
72 setLayout(new GridLayout(2, false));
73 setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
75 createResourceText(this);
76 createDropLabel(this);
81 public int getDepth() {
85 public void setDepth(int depth) {
88 if(depth == this.depth)
94 public Style getGraphStyle() {
98 public void setGraphStyle(Style style) {
99 if (this.style == style)
106 protected void initializeCSS() {
111 public Browser createBrowser(Composite parent) {
116 public GraphvizComponent2 createGraph(Composite parent) {
118 graph.setRankdir("LR"); //$NON-NLS-1$
119 graphVizComponent = new GraphvizComponent2(parent, SWT.NONE);
120 SwingUtilities.invokeLater(new Runnable() {
124 graphVizComponent.getCanvas().addMouseListener(new MouseListener() {
127 public void mouseReleased(MouseEvent arg0) {
132 public void mousePressed(MouseEvent arg0) {
137 public void mouseExited(MouseEvent arg0) {
142 public void mouseEntered(MouseEvent arg0) {
147 public void mouseClicked(MouseEvent arg0) {
148 if (arg0.getClickCount() > 1) {
149 Point p = arg0.getPoint();
157 GridDataFactory.fillDefaults().span(2, 1).grab(true, true).applyTo(graphVizComponent);
159 return graphVizComponent;
162 protected void pick(Point p) {
164 AffineTransform at = ((ViewerCanvas)graphVizComponent.getCanvas()).getTransform();
165 Point2D pickPoint = new Point2D.Double();
167 at.inverseTransform(new Point2D.Double((double)p.x,(double)p.y), pickPoint);
168 } catch (NoninvertibleTransformException e) {
172 Collection<IGraphPart> parts = graphVizComponent.getDrawable().pick(pickPoint);
173 for (IGraphPart part : parts) {
174 if (part instanceof Node) {
175 Resource r = nodeMap.getLeft((Node)part);
176 if (r != null && !r.equals(getDebuggerLocation())) {
183 for (IGraphPart part : parts) {
184 if (part instanceof Edge) {
185 Resource r = edgeMap2.get(part);
186 if (r != null && !r.equals(getDebuggerLocation())) {
194 protected Node getOrCreate(Resource r) {
195 Node n = nodeMap.getRight(r);
198 if (!r.isPersistent()) {
199 n.setShape("box"); //$NON-NLS-1$
200 n.setFontColor("blue"); //$NON-NLS-1$
207 @SuppressWarnings("unused")
208 protected void appendLabel(Node node, String text) {
209 String label = node.get("label"); //$NON-NLS-1$
211 if (label == null || label.length() == 0)
212 label = text;//escape(text);
214 label = label.substring(1,label.length()-1);
215 label += "<br/>"+text; //$NON-NLS-1$
217 label = "<" + label + ">"; //$NON-NLS-1$ //$NON-NLS-2$
219 if (label == null || label.length() == 0)
222 label += " "+text; //$NON-NLS-1$
226 node.setLabel(label);
232 protected synchronized void updateContent(final ReadGraph g, Resource... resources) throws DatabaseException {
233 L0 = Layer0.getInstance(g);
236 graph.setRankdir("LR"); //$NON-NLS-1$
244 //StringBuffer content = new StringBuffer();
246 // Generate HTML -page
247 // content.append("<html><head>" + getHead() + "</head>\n");
248 // content.append("<body>\n");
249 // content.append("<div id=\"mainContent\">\n");
251 // content.append("</div>\n");
252 //content.append("</body></html>\n");
253 createContent(g, depth, resources);
256 //graph.write(System.out);
257 graphVizComponent.setGraph(graph,style.toString());
260 private void createContent(final ReadGraph g, int iter, Resource... resources) throws DatabaseException {
263 for (Resource r : resources) {
266 if (processed.contains(r))
268 Node node = getResourceRef(g, r);
269 if (r.equals(getDebuggerLocation())) {
270 node.setFillColor("#aaffaa"); //$NON-NLS-1$
271 node.setStyle("filled"); //$NON-NLS-1$
273 //Node node = getOrCreate(r);
280 uri = g.syncRequest(new ResourceToPossibleURI(r));
281 } catch (Exception e) {
283 uri = "Cannot get URI: " + e.getMessage(); //$NON-NLS-1$
286 appendLabel(node, "URI: " + uri); //$NON-NLS-1$
287 //content.append("\t\t<div class=\"monospaced\">" + uri + "</div><br/>");
289 Collection<Statement> statements = g.getStatements(r, L0.IsWeaklyRelatedTo);
290 HashMultiMap<Resource, Resource[]> map = new HashMultiMap<Resource, Resource[]>();
291 for(org.simantics.db.Statement statement : statements) {
292 Resource predicate = null;
293 Resource subject = null;
296 predicate = statement.getPredicate();
297 subject = statement.getSubject();
298 obj = statement.getObject();
299 map.add(predicate, new Resource[] {subject, obj});
300 } catch (Throwable e) {
302 ErrorLogger.defaultLogError("Cannot find statement " + subject + " " + predicate + " " + obj, e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
305 ClusteringSupport support = g.getSession().getService(ClusteringSupport.class);
306 //content.append("<h3>" + " ["+ r.getResourceId() + "-" + support.getCluster(r) + "] " + "</h3>\n");
307 appendLabel(node, " ["+ r.getResourceId() + "-" + support.getCluster(r) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
309 //content.append("<table>\n");
310 //content.append("<tr><th>Predicate</th><th>Object</th></tr>");
311 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Basic information</td></tr>");
313 boolean isOrderedSet = g.isInstanceOf(r, L0.OrderedSet);
316 // BASIC INFORMATION:
318 new Resource[] {L0.HasName, L0.InstanceOf,
319 L0.Inherits, L0.SubrelationOf,
320 L0.ConsistsOf, L0.PartOf})
321 if (map.containsKey(pred))
322 updatePred(node, g, r, pred, map.remove(pred));
325 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Tags</td></tr>");
326 for(Statement stm : statements) {
327 if(stm.getSubject().equals(stm.getObject())) {
328 updateTag(node, g, r, stm.getPredicate(), "Tag"); //$NON-NLS-1$
329 map.remove(stm.getPredicate());
334 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered Sets</td></tr>");
335 for(Statement stm : statements) {
336 Resource predicate = stm.getPredicate();
337 if(g.isInstanceOf(stm.getPredicate(), L0.OrderedSet)) {
338 updateTag(node, g, r, stm.getPredicate(), "Ordered Set"); //$NON-NLS-1$
339 map.remove(stm.getPredicate());
341 Resource inverse = g.getPossibleInverse(predicate);
342 if (inverse != null) {
343 if(g.isInstanceOf(inverse, L0.OrderedSet)) {
344 map.remove(stm.getPredicate());
347 // FIXME : should we inform missing inverse
352 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Is Related To</td></tr>");
354 // ELEMENTS OF ORDERED SET
356 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Ordered set</td></tr>");
358 updateOrderedSet(node, g, r);
359 } catch (ValidationException e) {
360 //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>");
364 // IS RELATED TO (other)
365 Resource[] preds = map.keySet().toArray(new Resource[0]);
366 final Map<Resource, String> strmap = new HashMap<Resource, String>(preds.length);
367 for(Resource pred : preds) {
368 String str = htmlEscape(getResourceName(g, pred));
370 str = "<null>"; //$NON-NLS-1$
371 strmap.put(pred, str);
373 Arrays.sort(preds, new Comparator<Resource>() {
375 public int compare(Resource o1, Resource o2) {
376 return strmap.get(o1).compareTo(strmap.get(o2));
379 for(Resource pred : preds)
380 if(g.isSubrelationOf(pred, L0.IsRelatedTo))
381 updatePred(node, g, r, pred, map.get(pred));
384 //content.append("<tr><td class=\"subtitle\" colspan=\"2\">Other statements</td></tr>");
385 for(Resource pred : preds)
386 if(!g.isSubrelationOf(pred, L0.IsRelatedTo))
387 updatePred(node, g, r, pred, map.get(pred));
388 // content.append("</table>\n");
390 Resource objects[] = new Resource[statements.size()];
392 for (Statement stm : statements) {
393 objects[i] = stm.getObject();
396 createContent(g, iter-1, objects);
400 private Node getResourceRef(ReadGraph graph, Resource r) throws DatabaseException {
401 Node node = nodeMap.getRight(r);
403 node = getOrCreate(r);
404 appendLabel(node, htmlEscape(getResourceName(graph, r)));
409 private class NodeObject {
412 @SuppressWarnings("unused")
417 private void updatePred(Node node, ReadGraph graph, Resource subj, Resource pred, List<Resource[]> stats) throws DatabaseException {
418 // Generate output content from statements
419 NodeObject[] objects = new NodeObject[stats.size()];
420 for (int i = 0; i < stats.size(); ++i) {
421 Resource stmSubject = stats.get(i)[0];
422 Resource object = stats.get(i)[1];
424 objects[i] = new NodeObject();
425 objects[i].r = object;
426 objects[i].name = htmlEscape( getResourceName(graph, object) );
427 objects[i].node = getResourceRef(graph, object);
429 // Make a note if the statement was acquired.
430 if(!stmSubject.equals(subj)) {
431 Node asserted = getResourceRef(graph, stmSubject);
432 Edge e = new Edge(this.graph, objects[i].node, asserted);
433 e.setLabel(Messages.GraphicalDebugger_AssertedIn);
435 objects[i].node.setFillColor("#ffaaaa"); //$NON-NLS-1$
436 objects[i].node.setStyle("filled"); //$NON-NLS-1$
440 // Sort statements by object name
441 Arrays.sort(objects, new Comparator<NodeObject>() {
443 public int compare(NodeObject o1, NodeObject o2) {
444 return o1.name.compareTo(o2.name);
448 String predName = getResourceName(graph, pred);
450 for (int i = 0; i < objects.length; ++i) {
451 Edge e = new Edge(this.graph,node, objects[i].node);
452 e.setLabel(predName);
453 edgeMap.add(pred, e);
454 edgeMap2.put(e, pred);
455 //content.append("<tr>");
458 //content.append("<td rowspan=\"" + objects.length + "\" valign=\"top\">" + getResourceRef(graph, pred) + "</td>");
461 //if (objects[i][3] == null) content.append("<td>");
462 //else content.append("<td class=\"acquired\">");
464 //content.append(objects[i][2]);
465 //if (objects[i][3] != null)
466 // content.append(objects[i][3]);
468 //content.append("</td>");
470 // Statement remove -link column
471 // Only allowed for non-acquired statements.
472 //if (objects[i][3] == null) {
473 // content.append("<td class=\"remove\">");
474 // content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0])));
475 // content.append("</td>");
477 //content.append("</tr>");
481 private void updateTag(Node node, ReadGraph graph, Resource subj, Resource tag, String title) throws DatabaseException {
483 // Generate output content from statements
484 Node ref = getResourceRef(graph, tag);
485 Edge e = new Edge(this.graph, node, ref);
488 // content.append("<tr>");
489 // content.append("<td rowspan=\"1\" colspan=\"2\" valign=\"top\">" + ref + "</td>");
490 // content.append("<td class=\"remove\">");
491 // content.append(getStatementRemoveRef(subj, tag, subj));
492 // content.append("</td>");
493 // content.append("</tr>");
499 private void updateOrderedSet(Node node, ReadGraph graph, Resource subj) throws DatabaseException {
501 // StringBuffer content = new StringBuffer();
502 //List<String> list = new ArrayList<String>();
503 List<Node> list = new ArrayList<Node>();
507 cur = OrderedSetUtils.next(graph, subj, cur);
508 } catch(DatabaseException e) {
509 Edge edge = new Edge(this.graph, node, node);
510 edge.setLabel(Messages.GraphicalDebugger_BrockenOrderedSet);
511 //list.add("<span style=\"color:red;font-weight:bold\">BROKEN ORDERED SET:<br/></span><span style=\"color:red\">" + e.getMessage() + "</span>");
512 // Resource inv = graph.getPossibleInverse(subj);
513 // for(Statement stat : graph.getStatements(cur, L0.IsRelatedTo)) {
514 // if(stat.getSubject().equals(cur)) {
515 // if(stat.getPredicate().equals(subj)) {
516 // list.add("next " + getResourceRef(graph, stat.getObject()));
518 // else if(stat.getPredicate().equals(inv)) {
519 // list.add("prev " + getResourceRef(graph, stat.getObject()));
527 //list.add(getResourceName(graph, cur));
528 list.add(getResourceRef(graph, cur));
530 for (int i = 0; i < list.size() ; ++i) {
531 Edge e = new Edge(this.graph, node, list.get(i));
532 e.setLabel(NLS.bind(Messages.GraphicalDebugger_OrderedSetItem , i));
536 // content.append("<table>\n");
537 // for (int i = 0; i < list.size() ; ++i) {
538 // content.append("<tr>");
539 // // Predicate column
541 // content.append("<td rowspan=\"" + list.size() + "\" valign=\"top\">Ordered Set Elements</td>");
544 // content.append("<td>");
545 // content.append(list.get(i));
546 // content.append("</td>");
548 // content.append("</tr>");
550 // content.append("</table>\n");
551 // appendLabel(node, content.toString());