package org.simantics.debug.ui.graph; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.SwingUtilities; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.Statement; import org.simantics.db.common.uri.ResourceToPossibleURI; import org.simantics.db.common.utils.OrderedSetUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ValidationException; import org.simantics.db.service.ClusteringSupport; import org.simantics.debug.ui.GraphDebugger; import org.simantics.debug.ui.internal.HashMultiMap; import org.simantics.graphviz.Edge; import org.simantics.graphviz.Graph; import org.simantics.graphviz.IGraphPart; import org.simantics.graphviz.Node; import org.simantics.graphviz.drawable.ViewerCanvas; import org.simantics.graphviz.ui.GraphvizComponent2; import org.simantics.layer0.Layer0; import org.simantics.utils.datastructures.BijectionMap; import org.simantics.utils.datastructures.MapList; import org.simantics.utils.ui.ErrorLogger; public class GraphicalDebugger extends GraphDebugger { public enum Style{dot,neato,fdp,sfdp,twopi,circo}; private Graph graph; private BijectionMap nodeMap = new BijectionMap(); private MapList edgeMap = new MapList(); private Map edgeMap2 = new HashMap(); private Set processed = new HashSet(); private GraphvizComponent2 graphVizComponent; private int depth = 1; private Style style = Style.dot; public GraphicalDebugger(Composite parent, int style, final Session session, Resource resource) { super(parent, style, session, resource); } public void defaultInitializeUI() { setLayout(new GridLayout(2, false)); setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); createResourceText(this); createDropLabel(this); createGraph(this); } public int getDepth() { return depth; } public void setDepth(int depth) { if (depth < 1) return; if(depth == this.depth) return; this.depth = depth; refreshBrowser(); } public Style getGraphStyle() { return style; } public void setGraphStyle(Style style) { if (this.style == style) return; this.style = style; refreshBrowser(); } @Override protected void initializeCSS() { // do nothing } @Override public Browser createBrowser(Composite parent) { // do nothing return null; } public GraphvizComponent2 createGraph(Composite parent) { graph = new Graph(); graph.setRankdir("LR"); //$NON-NLS-1$ graphVizComponent = new GraphvizComponent2(parent, SWT.NONE); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { graphVizComponent.getCanvas().addMouseListener(new MouseListener() { @Override public void mouseReleased(MouseEvent arg0) { } @Override public void mousePressed(MouseEvent arg0) { } @Override public void mouseExited(MouseEvent arg0) { } @Override public void mouseEntered(MouseEvent arg0) { } @Override public void mouseClicked(MouseEvent arg0) { if (arg0.getClickCount() > 1) { Point p = arg0.getPoint(); pick(p); } } }); } }); GridDataFactory.fillDefaults().span(2, 1).grab(true, true).applyTo(graphVizComponent); refreshBrowser(); return graphVizComponent; } protected void pick(Point p) { AffineTransform at = ((ViewerCanvas)graphVizComponent.getCanvas()).getTransform(); Point2D pickPoint = new Point2D.Double(); try { at.inverseTransform(new Point2D.Double((double)p.x,(double)p.y), pickPoint); } catch (NoninvertibleTransformException e) { return; } Collection parts = graphVizComponent.getDrawable().pick(pickPoint); for (IGraphPart part : parts) { if (part instanceof Node) { Resource r = nodeMap.getLeft((Node)part); if (r != null && !r.equals(getDebuggerLocation())) { changeLocation(r); return; } } } for (IGraphPart part : parts) { if (part instanceof Edge) { Resource r = edgeMap2.get(part); if (r != null && !r.equals(getDebuggerLocation())) { changeLocation(r); return; } } } } protected Node getOrCreate(Resource r) { Node n = nodeMap.getRight(r); if (n == null) { n = new Node(graph); if (!r.isPersistent()) { n.setShape("box"); //$NON-NLS-1$ n.setFontColor("blue"); //$NON-NLS-1$ } nodeMap.map(r, n); } return n; } @SuppressWarnings("unused") protected void appendLabel(Node node, String text) { String label = node.get("label"); //$NON-NLS-1$ if (true) { if (label == null || label.length() == 0) label = text;//escape(text); else { label = label.substring(1,label.length()-1); label += "
"+text; //$NON-NLS-1$ } label = "<" + label + ">"; //$NON-NLS-1$ //$NON-NLS-2$ } else { if (label == null || label.length() == 0) label = text; else { label += " "+text; //$NON-NLS-1$ } } node.setLabel(label); } protected synchronized void updateContent(final ReadGraph g, Resource... resources) throws DatabaseException { L0 = Layer0.getInstance(g); graph = new Graph(); graph.setRankdir("LR"); //$NON-NLS-1$ nodeMap.clear(); edgeMap.clear(); edgeMap2.clear(); processed.clear(); //links.clear(); //StringBuffer content = new StringBuffer(); // Generate HTML -page // content.append("" + getHead() + "\n"); // content.append("\n"); // content.append("
\n"); // content.append("
\n"); //content.append("\n"); createContent(g, depth, resources); // Update content //graph.write(System.out); graphVizComponent.setGraph(graph,style.toString()); } private void createContent(final ReadGraph g, int iter, Resource... resources) throws DatabaseException { if (iter == 0) return; for (Resource r : resources) { if (r == null) continue; if (processed.contains(r)) continue; Node node = getResourceRef(g, r); if (r.equals(getDebuggerLocation())) { node.setFillColor("#aaffaa"); //$NON-NLS-1$ node.setStyle("filled"); //$NON-NLS-1$ } //Node node = getOrCreate(r); processed.add(r); String uri = null; try { uri = g.syncRequest(new ResourceToPossibleURI(r)); } catch (Exception e) { e.printStackTrace(); uri = "Cannot get URI: " + e.getMessage(); //$NON-NLS-1$ } if (uri != null) appendLabel(node, "URI: " + uri); //$NON-NLS-1$ //content.append("\t\t
" + uri + "

"); Collection statements = g.getStatements(r, L0.IsWeaklyRelatedTo); HashMultiMap map = new HashMultiMap(); for(org.simantics.db.Statement statement : statements) { Resource predicate = null; Resource subject = null; Resource obj = null; try { predicate = statement.getPredicate(); subject = statement.getSubject(); obj = statement.getObject(); map.add(predicate, new Resource[] {subject, obj}); } catch (Throwable e) { e.printStackTrace(); ErrorLogger.defaultLogError("Cannot find statement " + subject + " " + predicate + " " + obj, e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } ClusteringSupport support = g.getSession().getService(ClusteringSupport.class); //content.append("

" + " ["+ r.getResourceId() + "-" + support.getCluster(r) + "] " + "

\n"); appendLabel(node, " ["+ r.getResourceId() + "-" + support.getCluster(r) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //content.append("\n"); //content.append(""); //content.append(""); boolean isOrderedSet = g.isInstanceOf(r, L0.OrderedSet); map.remove(r); // BASIC INFORMATION: for (Resource pred : new Resource[] {L0.HasName, L0.InstanceOf, L0.Inherits, L0.SubrelationOf, L0.ConsistsOf, L0.PartOf}) if (map.containsKey(pred)) updatePred(node, g, r, pred, map.remove(pred)); // TAGS //content.append(""); for(Statement stm : statements) { if(stm.getSubject().equals(stm.getObject())) { updateTag(node, g, r, stm.getPredicate(), "Tag"); //$NON-NLS-1$ map.remove(stm.getPredicate()); } } // ORDERED SETS //content.append(""); for(Statement stm : statements) { Resource predicate = stm.getPredicate(); if(g.isInstanceOf(stm.getPredicate(), L0.OrderedSet)) { updateTag(node, g, r, stm.getPredicate(), "Ordered Set"); //$NON-NLS-1$ map.remove(stm.getPredicate()); } Resource inverse = g.getPossibleInverse(predicate); if (inverse != null) { if(g.isInstanceOf(inverse, L0.OrderedSet)) { map.remove(stm.getPredicate()); } } else { // FIXME : should we inform missing inverse } } // IS RELATED TO //content.append(""); // ELEMENTS OF ORDERED SET if(isOrderedSet) { //content.append(""); try { updateOrderedSet(node, g, r); } catch (ValidationException e) { //content.append(""); } } // IS RELATED TO (other) Resource[] preds = map.keySet().toArray(new Resource[0]); final Map strmap = new HashMap(preds.length); for(Resource pred : preds) { String str = htmlEscape(getResourceName(g, pred)); if(str == null) str = ""; //$NON-NLS-1$ strmap.put(pred, str); } Arrays.sort(preds, new Comparator() { @Override public int compare(Resource o1, Resource o2) { return strmap.get(o1).compareTo(strmap.get(o2)); } }); for(Resource pred : preds) if(g.isSubrelationOf(pred, L0.IsRelatedTo)) updatePred(node, g, r, pred, map.get(pred)); // OTHER STATEMENTS //content.append(""); for(Resource pred : preds) if(!g.isSubrelationOf(pred, L0.IsRelatedTo)) updatePred(node, g, r, pred, map.get(pred)); // content.append("
PredicateObject
Basic information
Tags
Ordered Sets
Is Related To
Ordered set
BROKEN ORDERED SET:
" + e.getMessage() + "
Other statements
\n"); Resource objects[] = new Resource[statements.size()]; int i = 0; for (Statement stm : statements) { objects[i] = stm.getObject(); i++; } createContent(g, iter-1, objects); } } private Node getResourceRef(ReadGraph graph, Resource r) throws DatabaseException { Node node = nodeMap.getRight(r); if (node == null) { node = getOrCreate(r); appendLabel(node, htmlEscape(getResourceName(graph, r))); } return node; } private class NodeObject { public String name; public Node node; @SuppressWarnings("unused") public Resource r; } private void updatePred(Node node, ReadGraph graph, Resource subj, Resource pred, List stats) throws DatabaseException { // Generate output content from statements NodeObject[] objects = new NodeObject[stats.size()]; for (int i = 0; i < stats.size(); ++i) { Resource stmSubject = stats.get(i)[0]; Resource object = stats.get(i)[1]; objects[i] = new NodeObject(); objects[i].r = object; objects[i].name = htmlEscape( getResourceName(graph, object) ); objects[i].node = getResourceRef(graph, object); // Make a note if the statement was acquired. if(!stmSubject.equals(subj)) { Node asserted = getResourceRef(graph, stmSubject); Edge e = new Edge(this.graph, objects[i].node, asserted); e.setLabel(Messages.GraphicalDebugger_AssertedIn); objects[i].node.setFillColor("#ffaaaa"); //$NON-NLS-1$ objects[i].node.setStyle("filled"); //$NON-NLS-1$ } } // Sort statements by object name Arrays.sort(objects, new Comparator() { @Override public int compare(NodeObject o1, NodeObject o2) { return o1.name.compareTo(o2.name); } }); String predName = getResourceName(graph, pred); // Output table rows for (int i = 0; i < objects.length; ++i) { Edge e = new Edge(this.graph,node, objects[i].node); e.setLabel(predName); edgeMap.add(pred, e); edgeMap2.put(e, pred); //content.append(""); // Predicate column //if (i == 0) //content.append("" + getResourceRef(graph, pred) + ""); // Object column //if (objects[i][3] == null) content.append(""); //else content.append(""); //content.append(objects[i][2]); //if (objects[i][3] != null) // content.append(objects[i][3]); //content.append(""); // Statement remove -link column // Only allowed for non-acquired statements. //if (objects[i][3] == null) { // content.append(""); // content.append(getStatementRemoveRef(subj, pred, links.getRight(objects[i][0]))); // content.append(""); //} //content.append(""); } } private void updateTag(Node node, ReadGraph graph, Resource subj, Resource tag, String title) throws DatabaseException { // Generate output content from statements Node ref = getResourceRef(graph, tag); Edge e = new Edge(this.graph, node, ref); e.setLabel(title); // content.append(""); // content.append("" + ref + ""); // content.append(""); // content.append(getStatementRemoveRef(subj, tag, subj)); // content.append(""); // content.append(""); } private void updateOrderedSet(Node node, ReadGraph graph, Resource subj) throws DatabaseException { // StringBuffer content = new StringBuffer(); //List list = new ArrayList(); List list = new ArrayList(); Resource cur = subj; while(true) { try { cur = OrderedSetUtils.next(graph, subj, cur); } catch(DatabaseException e) { Edge edge = new Edge(this.graph, node, node); edge.setLabel(Messages.GraphicalDebugger_BrockenOrderedSet); //list.add("BROKEN ORDERED SET:
" + e.getMessage() + ""); // Resource inv = graph.getPossibleInverse(subj); // for(Statement stat : graph.getStatements(cur, L0.IsRelatedTo)) { // if(stat.getSubject().equals(cur)) { // if(stat.getPredicate().equals(subj)) { // list.add("next " + getResourceRef(graph, stat.getObject())); // } // else if(stat.getPredicate().equals(inv)) { // list.add("prev " + getResourceRef(graph, stat.getObject())); // } // } // } // break; } if(cur.equals(subj)) break; //list.add(getResourceName(graph, cur)); list.add(getResourceRef(graph, cur)); } for (int i = 0; i < list.size() ; ++i) { Edge e = new Edge(this.graph, node, list.get(i)); e.setLabel(NLS.bind(Messages.GraphicalDebugger_OrderedSetItem , i)); } // Output table rows // content.append("\n"); // for (int i = 0; i < list.size() ; ++i) { // content.append(""); // // Predicate column // if (i == 0) // content.append(""); // // // Object column // content.append(""); // // content.append(""); // } // content.append("
Ordered Set Elements"); // content.append(list.get(i)); // content.append("
\n"); // appendLabel(node, content.toString()); } }