GraphManager.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. package de.tu_darmstadt.informatik.tk.scopviz.graphs;
  2. import java.util.Collection;
  3. import java.util.HashMap;
  4. import java.util.LinkedList;
  5. import org.graphstream.graph.Edge;
  6. import org.graphstream.graph.Element;
  7. import org.graphstream.graph.Node;
  8. import org.graphstream.ui.swingViewer.ViewPanel;
  9. import org.graphstream.ui.view.Viewer;
  10. import org.graphstream.ui.view.ViewerPipe;
  11. import de.tu_darmstadt.informatik.tk.scopviz.debug.Debug;
  12. import de.tu_darmstadt.informatik.tk.scopviz.main.CreationMode;
  13. import de.tu_darmstadt.informatik.tk.scopviz.main.Layer;
  14. import de.tu_darmstadt.informatik.tk.scopviz.main.Main;
  15. import de.tu_darmstadt.informatik.tk.scopviz.ui.GraphDisplayManager;
  16. import de.tu_darmstadt.informatik.tk.scopviz.ui.PropertiesManager;
  17. import de.tu_darmstadt.informatik.tk.scopviz.ui.StylesheetManager;
  18. import de.tu_darmstadt.informatik.tk.scopviz.ui.handlers.MyMouseManager;
  19. /**
  20. * Interface between GUI and internal Graph representation. Manages internal
  21. * representation of the Graph to accommodate creation and deletion of nodes and
  22. * edges.
  23. *
  24. * @author Jascha Bohne
  25. * @version 3.0.0.0
  26. *
  27. */
  28. public class GraphManager {
  29. /** String for the processing enabled type of node. */
  30. public static final String UI_CLASS_PROCESSING_ENABLED = "procEn";
  31. /** The Graph this instance of GraphManager manages. */
  32. protected MyGraph g;
  33. /**
  34. * The Stylesheet for this Graph, excluding parts that can be set by
  35. * NodeGraphics.
  36. */
  37. protected String stylesheet = "";
  38. /** The last Node that was deleted. */
  39. protected Node deletedNode;
  40. /** The last Edge that was deleted. */
  41. protected LinkedList<Edge> deletedEdges = new LinkedList<>();
  42. /** The currently selected Node, mutually exclusive with selectedEdgeID. */
  43. protected String selectedNodeID = null;
  44. /** The currently selected Edge, mutually exclusive with selectedNodeID. */
  45. protected String selectedEdgeID = null;
  46. /** The ViewPanel the Graph is drawn in. */
  47. protected ViewPanel view;
  48. /** The Path on Disk the Graph will be saved to. */
  49. protected String currentPath;
  50. /** The Viewer the Graph provides, grants Access to Camera Manipulation. */
  51. protected Viewer viewer;
  52. /**
  53. * The Pipe that notifies the underlying Graph of any Changes within the
  54. * graphic Representation.
  55. */
  56. protected ViewerPipe fromViewer;
  57. /**
  58. * The Id of the Node that was last clicked.
  59. */
  60. protected String lastClickedID;
  61. /**
  62. * Creates a new Manager for the given graph.
  63. *
  64. * @param graph
  65. * the graph this visualizer should handle
  66. */
  67. public GraphManager(MyGraph graph) {
  68. g = graph;
  69. /* Viewer */ viewer = new Viewer(g, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
  70. view = viewer.addDefaultView(false);
  71. viewer.setCloseFramePolicy(Viewer.CloseFramePolicy.EXIT);
  72. /* ViewerPipe */fromViewer = viewer.newViewerPipe();
  73. view.setMouseManager(new MyMouseManager(this));
  74. fromViewer.addSink(graph);
  75. fromViewer.removeElementSink(graph);
  76. }
  77. /**
  78. * Deletes the Node corresponding to the given ID from the Graph. The
  79. * referenced Graph is modified directly. Will throw an
  80. * ElementNotFoundException, when the Node is not Found Will also remove all
  81. * Edges connected to the given Node
  82. *
  83. * @param id
  84. * the ID of the node that will be removed
  85. */
  86. public void deleteNode(final String id) {
  87. deletedEdges.removeAll(deletedEdges);
  88. deletedNode = null;
  89. // Edges have to be deleted first because they clear deletedNode
  90. // and need the Node to still be in the Graph
  91. deleteEdgesOfNode(id);
  92. deletedNode = g.removeNode(id);
  93. }
  94. /**
  95. * Deletes the Edge corresponding to the given ID from the Graph. The
  96. * referenced Graph is modified directly. Will throw an
  97. * ElementNotFoundException, when the Edge is not Found
  98. *
  99. * @param id
  100. * the ID of the Edge that will be removed
  101. */
  102. public void deleteEdge(final String id) {
  103. deselect();
  104. deletedEdges.removeAll(deletedEdges);
  105. deletedNode = null;
  106. deletedEdges.add(g.removeEdge(id));
  107. }
  108. /**
  109. * Deletes all Edges connected to the given Node. The referenced Graph is
  110. * modified directly. Will throw an ElementNotFoundException if the Node is
  111. * not Found
  112. *
  113. * @param id
  114. * the Id of the Node, whose Edges shall be removed
  115. */
  116. protected void deleteEdgesOfNode(final String id) {
  117. deselect();
  118. Node node = g.getNode(id);
  119. deletedEdges.removeAll(deletedEdges);
  120. deletedNode = null;
  121. Edge[] temp = new Edge[0];
  122. temp = g.getEdgeSet().toArray(temp);
  123. for (Edge e : temp) {
  124. if (e.getSourceNode().equals(node) || e.getTargetNode().equals(node)) {
  125. // adds the Edge to the list of deleted Edges and remove sit
  126. // from the Graph
  127. deletedEdges.add(g.removeEdge(e));
  128. }
  129. }
  130. GraphHelper.propagateElementDeletion(g, deletedEdges);
  131. }
  132. /**
  133. * Undoes the last deleting operation on the given Graph. Deleting
  134. * operations are: deleteNode, deleteEdge and deleteEdgesOfNode. Only undoes
  135. * the last deleting operation even if that operation didn't change the
  136. * Graph
  137. */
  138. public void undelete() {
  139. String newId = "";
  140. HashMap<String, Object> attributes = new HashMap<String, Object>();
  141. if (deletedNode != null) {
  142. for (String s : deletedNode.getAttributeKeySet()) {
  143. attributes.put(s, deletedNode.getAttribute(s));
  144. }
  145. newId = Main.getInstance().getUnusedID();
  146. g.addNode(newId);
  147. g.getNode(newId).addAttributes(attributes);
  148. String origElement = GraphHelper.propagateElementUndeletion(g, deletedNode, null);
  149. if(origElement != null){
  150. g.getNode(newId).addAttribute("originalElement", origElement);
  151. }
  152. }
  153. for (Edge e : deletedEdges) {
  154. String sourceId = null;
  155. String targetId = null;
  156. attributes = new HashMap<String, Object>();
  157. for (String s : e.getAttributeKeySet()) {
  158. attributes.put(s, e.getAttribute(s));
  159. }
  160. String id = Main.getInstance().getUnusedID();
  161. if (deletedNode != null) {
  162. sourceId = (e.getSourceNode().getId().equals(deletedNode.getId())) ? newId : e.getSourceNode().getId();
  163. targetId = (e.getTargetNode().getId().equals(deletedNode.getId())) ? newId : e.getTargetNode().getId();
  164. } else {
  165. sourceId = e.getSourceNode().getId();
  166. targetId = e.getTargetNode().getId();
  167. }
  168. g.addEdge(id, sourceId, targetId, e.isDirected());
  169. g.getEdge(id).addAttributes(attributes);
  170. String origElement = GraphHelper.propagateElementUndeletion(g, e, g.getNode(newId).getAttribute("originalElement"));
  171. if(origElement != null){
  172. g.getEdge(id).addAttribute("originalElement", origElement);
  173. }
  174. }
  175. deletedEdges = new LinkedList<>();
  176. deletedNode = null;
  177. }
  178. /**
  179. * returns a View of the Graph. The View lives in the Swing Thread and the
  180. * Graph in the Main thread.
  181. *
  182. *
  183. * @return a View of the Graph, inheriting from JPanel
  184. */
  185. public ViewPanel getView() {
  186. return view;
  187. }
  188. /**
  189. * Returns the ID of the currently selected Node.
  190. *
  191. * @return the node's ID
  192. */
  193. public String getSelectedNodeID() {
  194. return selectedNodeID;
  195. }
  196. /**
  197. * Returns the ID of the currently selected Edge.
  198. *
  199. * @return the edge's ID
  200. */
  201. public String getSelectedEdgeID() {
  202. return selectedEdgeID;
  203. }
  204. /**
  205. * Selects the Node with the given ID, resets Edge selection.
  206. *
  207. * @param nodeID
  208. * the ID of the Node to select
  209. */
  210. public void selectNode(String nodeID) {
  211. if (nodeID != null && g.getNode(nodeID) != null) {
  212. deselect();
  213. this.selectedNodeID = nodeID;
  214. Node n = g.getNode(nodeID);
  215. // set selected node color to red
  216. if (!hasClass(n, UI_CLASS_PROCESSING_ENABLED)
  217. || !GraphDisplayManager.getCurrentLayer().equals(Layer.MAPPING)) {
  218. n.changeAttribute("ui.style", "fill-color : #F00; size: 15px;");
  219. PropertiesManager.setItemsProperties();
  220. }
  221. }
  222. }
  223. /**
  224. * Selects the Edge with the given ID, resets Node selection.
  225. *
  226. * @param edgeID
  227. * the ID of the Edge to select
  228. */
  229. public void selectEdge(String edgeID) {
  230. if (edgeID != null && g.getEdge(edgeID) != null) {
  231. deselect();
  232. this.selectedEdgeID = edgeID;
  233. addClass(edgeID, "selected");
  234. PropertiesManager.setItemsProperties();
  235. }
  236. }
  237. /**
  238. * Deselect any currently selected nodes or edges.
  239. */
  240. // TODO call this before save
  241. protected void deselect() {
  242. // Set last selected Edge Color to Black
  243. if (getSelectedEdgeID() != null && g.getEdge(getSelectedEdgeID()) != null) {
  244. removeClass(getSelectedEdgeID(), "selected");
  245. }
  246. // Set last selected Node color to black
  247. else if (getSelectedNodeID() != null && g.getNode(getSelectedNodeID()) != null) {
  248. Node n = g.getNode(getSelectedNodeID());
  249. if (!hasClass(n, UI_CLASS_PROCESSING_ENABLED)
  250. || !GraphDisplayManager.getCurrentLayer().equals(Layer.MAPPING)) {
  251. String nodeType = n.getAttribute("ui.class");
  252. n.removeAttribute("ui.style");
  253. n.changeAttribute("ui.style", "fill-color: #000000; size: 15px;");
  254. n.changeAttribute("ui.class", nodeType.split("_")[0]);
  255. }
  256. }
  257. this.selectedNodeID = null;
  258. this.selectedEdgeID = null;
  259. }
  260. /**
  261. * Returns a reference to the Graph object managed by this visualizer.
  262. *
  263. * @return the graph
  264. */
  265. public MyGraph getGraph() {
  266. return g;
  267. }
  268. /**
  269. * Zooms in the view of the graph by 5 percent.
  270. */
  271. public void zoomIn() {
  272. zoom(-0.05);
  273. }
  274. /**
  275. * Zooms out the view of the graph by 5 percent.
  276. */
  277. public void zoomOut() {
  278. zoom(0.05);
  279. }
  280. /**
  281. * Zooms the view by the given Amount, positive values zoom out, negative
  282. * values zoom in.
  283. *
  284. * @param amount
  285. * the amount of zoom, should usually be between -0.2 and 0.2 for
  286. * reasonable zoom.
  287. */
  288. public void zoom(double amount) {
  289. view.getCamera().setViewPercent(view.getCamera().getViewPercent() * (1 + amount));
  290. }
  291. /**
  292. * Pumps the Pipe from the graphical Representation to the underlying Graph,
  293. * propagating all Changes made.
  294. */
  295. public void pumpIt() {
  296. fromViewer.pump();
  297. }
  298. @Override
  299. public String toString() {
  300. return "Visualizer for Graph \"" + g.getId() + "\"";
  301. }
  302. /**
  303. * Returns the current Save Path on Disk for the Graph.
  304. *
  305. * @return the current Path
  306. */
  307. public String getCurrentPath() {
  308. return currentPath;
  309. }
  310. /**
  311. * Sets the Save Path.
  312. *
  313. * @param currentPath
  314. * the new Path to set
  315. */
  316. public void setCurrentPath(String currentPath) {
  317. this.currentPath = currentPath;
  318. }
  319. /**
  320. * Adds a <b>Copy</b> of the given Edge to the graph. The Copy retains the
  321. * ID and all attributes.
  322. *
  323. * @param e
  324. * the Edge to be added to the graph
  325. */
  326. public void addEdge(Edge e) {
  327. HashMap<String, Object> attributes = new HashMap<>();
  328. for (String s : e.getAttributeKeySet()) {
  329. attributes.put(s, e.getAttribute(s));
  330. }
  331. g.addEdge(e.getId(), (Node) e.getSourceNode(), (Node) e.getTargetNode(), e.isDirected());
  332. g.getEdge(e.getId()).addAttributes(attributes);
  333. }
  334. /**
  335. * Adds a <b>Copy</b> of the given Node to the graph. The Copy retains the
  336. * ID and all attributes.
  337. *
  338. * @param n
  339. * the Node to be added to the graph
  340. */
  341. public void addNode(Node n) {
  342. HashMap<String, Object> attributes = new HashMap<>();
  343. for (String s : n.getAttributeKeySet()) {
  344. attributes.put(s, deletedNode.getAttribute(s));
  345. }
  346. g.addNode(n.getId());
  347. g.getNode(n.getId()).addAttributes(attributes);
  348. }
  349. /**
  350. * Returns the smallest X Coordinate of any Node in the Graph.
  351. *
  352. * @return the smallest X Coordinate in the Graph
  353. */
  354. public double getMinX() {
  355. return g.getMinX();
  356. }
  357. /**
  358. * Returns the biggest X Coordinate of any Node in the Graph.
  359. *
  360. * @return the biggest X Coordinate in the Graph
  361. */
  362. public double getMaxX() {
  363. return g.getMaxX();
  364. }
  365. /**
  366. * Returns the smallest Y Coordinate of any Node in the Graph.
  367. *
  368. * @return the smallest Y Coordinate in the Graph
  369. */
  370. public double getMinY() {
  371. return g.getMinY();
  372. }
  373. /**
  374. * Returns the biggest Y Coordinate of any Node in the Graph.
  375. *
  376. * @return the biggest Y Coordinate in the Graph
  377. */
  378. public double getMaxY() {
  379. return g.getMaxY();
  380. }
  381. /**
  382. * Returns the Stylesheet used by the Graph.
  383. *
  384. * @return the Stylesheet in use
  385. */
  386. public String getStylesheet() {
  387. return stylesheet;
  388. }
  389. /**
  390. * Sets the Stylesheet to be used by the Graph.
  391. *
  392. * @param stylesheet
  393. * the new stylesheet to use
  394. */
  395. public void setStylesheet(String stylesheet) {
  396. this.stylesheet = stylesheet;
  397. g.removeAttribute("ui.stylesheet");
  398. String completeStylesheet = stylesheet;
  399. completeStylesheet = completeStylesheet.concat(StylesheetManager.getNodeStylesheet());
  400. completeStylesheet = completeStylesheet
  401. .concat(StylesheetManager.getLayerStyle((Layer) g.getAttribute("layer")));
  402. g.addAttribute("ui.stylesheet", completeStylesheet);
  403. }
  404. /**
  405. * adds the given listener to the underlying graph the listener will be
  406. * notified, when an Edge is added.
  407. *
  408. * @param e
  409. * the EdgeCreatedListener
  410. */
  411. public void addEdgeCreatedListener(EdgeCreatedListener e) {
  412. ((MyGraph) g).addEdgeCreatedListener(e);
  413. }
  414. /**
  415. * adds the given listener to the underlying graph the listener will be
  416. * notified, when a Node is added.
  417. *
  418. * @param n
  419. * the NodeCreatedListener
  420. */
  421. public void addNodeCreatedListener(NodeCreatedListener n) {
  422. ((MyGraph) g).addNodeCreatedListener(n);
  423. }
  424. /**
  425. * Updates the Stylesheet, causing any changes to it to come into effect.
  426. */
  427. public void updateStylesheet() {
  428. setStylesheet(this.stylesheet);
  429. }
  430. /**
  431. * Sets typeofNode as the ui.class of all Nodes.
  432. *
  433. */
  434. public void convertUiClass() {
  435. Collection<Node> allNodes = g.getNodeSet();
  436. for (Node n : allNodes) {
  437. if (n.hasAttribute("typeofNode")) {
  438. n.addAttribute("ui.class", n.getAttribute("typeofNode").toString());
  439. }
  440. }
  441. }
  442. /**
  443. * Create Edges based on CreateMode.
  444. *
  445. * @param id
  446. * The ID for the newly created Edge
  447. */
  448. public void createEdges(String id) {
  449. switch (Main.getInstance().getCreationMode()) {
  450. case CREATE_DIRECTED_EDGE:
  451. case CREATE_UNDIRECTED_EDGE:
  452. if (lastClickedID == null) {
  453. lastClickedID = id;
  454. if (!selectNodeForEdgeCreation(lastClickedID)) {
  455. lastClickedID = null;
  456. }
  457. } else if (id.equals(lastClickedID) || createEdge(id, lastClickedID)) {
  458. deselectNodesAfterEdgeCreation(lastClickedID);
  459. lastClickedID = null;
  460. }
  461. break;
  462. default:
  463. break;
  464. }
  465. PropertiesManager.setItemsProperties();
  466. }
  467. /**
  468. * creates a edge between two nodes.
  469. *
  470. * @author MW
  471. * @param to
  472. * ID of the destination node
  473. * @param from
  474. * ID of the origin node
  475. * @return true if the edge was created. false otherwise
  476. */
  477. protected boolean createEdge(String to, String from) {
  478. if (getGraph().getNode(from).hasEdgeBetween(to))
  479. return false;
  480. String newID = Main.getInstance().getUnusedID();
  481. if (Main.getInstance().getCreationMode() == CreationMode.CREATE_DIRECTED_EDGE) {
  482. getGraph().addEdge(newID, from, to, true);
  483. Debug.out("Created an directed edge with Id " + newID + " between " + from + " and " + to);
  484. } else {
  485. getGraph().addEdge(newID, from, to);
  486. Debug.out("Created an undirected edge with Id " + newID + " between " + from + " and " + to);
  487. }
  488. selectEdge(newID);
  489. return true;
  490. }
  491. /**
  492. * Selects a Node as the starting point for creating a new Edge.
  493. *
  494. * @param nodeID
  495. * the ID of the Node to select
  496. */
  497. protected boolean selectNodeForEdgeCreation(String nodeID) {
  498. deselect();
  499. Node n = getGraph().getNode(nodeID);
  500. if (!hasClass(n, UI_CLASS_PROCESSING_ENABLED) || !GraphDisplayManager.getCurrentLayer().equals(Layer.MAPPING)) {
  501. n.changeAttribute("ui.style", "fill-color:green; size: 15px;");
  502. }
  503. return true;
  504. }
  505. /**
  506. * Reset the Selection of the Node after Edge has been successfully created.
  507. *
  508. * @param nodeID
  509. * the Id of the node to deselect.
  510. */
  511. protected void deselectNodesAfterEdgeCreation(String nodeID) {
  512. Node n = getGraph().getNode(nodeID);
  513. if (n == null) {
  514. return;
  515. }
  516. if (!hasClass(n, UI_CLASS_PROCESSING_ENABLED) || !GraphDisplayManager.getCurrentLayer().equals(Layer.MAPPING)) {
  517. n.removeAttribute("ui.style");
  518. n.changeAttribute("ui.style", "fill-color: #000000; size: 15px;");
  519. }
  520. }
  521. /**
  522. * Resets the selction of the Node for Edge selection
  523. */
  524. public void deselectEdgeCreationNodes() {
  525. if (lastClickedID != null)
  526. deselectNodesAfterEdgeCreation(lastClickedID);
  527. lastClickedID = null;
  528. }
  529. protected boolean addClass(String id, String className) {
  530. Element e = getGraph().getEdge(id);
  531. if (e == null)
  532. e = getGraph().getNode(id);
  533. if (e == null)
  534. return false;
  535. String eClass = e.getAttribute("ui.class");
  536. if (eClass == null || eClass.equals(""))
  537. eClass = className;
  538. else if (!(eClass.equals(className) || eClass.startsWith(className.concat(", "))
  539. || eClass.contains(", ".concat(className))))
  540. eClass = className.concat(", ").concat(eClass);
  541. e.addAttribute("ui.class", eClass);
  542. Debug.out("added " + className + ": " + eClass);
  543. return true;
  544. }
  545. protected boolean removeClass(String id, String className) {
  546. Element e = getGraph().getEdge(id);
  547. if (e == null)
  548. e = getGraph().getNode(id);
  549. if (e == null)
  550. return false;
  551. String eClass = e.getAttribute("ui.class");
  552. if (eClass == null || eClass.equals(className))
  553. eClass = "";
  554. else
  555. eClass = eClass.replace(className.concat(", "), "").replace(", ".concat(className), "");
  556. e.addAttribute("ui.class", eClass);
  557. Debug.out("removed " + className + ": " + eClass);
  558. return true;
  559. }
  560. protected boolean toggleClass(String id, String className) {
  561. Element e = getGraph().getEdge(id);
  562. if (e == null)
  563. e = getGraph().getNode(id);
  564. if (e == null)
  565. return false;
  566. String eClass = e.getAttribute("ui.class");
  567. if (eClass == null || !(eClass.equals(className) || eClass.startsWith(className.concat(", "))
  568. || eClass.contains(", ".concat(className))))
  569. return addClass(id, className);
  570. return removeClass(id, className);
  571. }
  572. protected boolean hasClass(String id, String className) {
  573. Element e = getGraph().getEdge(id);
  574. if (e == null)
  575. e = getGraph().getNode(id);
  576. if (e == null)
  577. return false;
  578. String eClass = e.getAttribute("ui.class");
  579. return (eClass != null && (eClass.equals(className) || eClass.startsWith(className.concat(", "))
  580. || eClass.contains(", ".concat(className))));
  581. }
  582. protected boolean hasClass(Edge e, String className) {
  583. if (e == null)
  584. return false;
  585. String eClass = e.getAttribute("ui.class");
  586. return (eClass != null && (eClass.equals(className) || eClass.startsWith(className.concat(", "))
  587. || eClass.contains(", ".concat(className))));
  588. }
  589. protected boolean hasClass(Node n, String className) {
  590. if (n == null)
  591. return false;
  592. String nClass = n.getAttribute("ui.class");
  593. /*
  594. * TODO: nochmal angucken, wenn CSS Manager steht, ist gerade gehackt
  595. * damit, Vorführung läuft. return (nClass != null &&
  596. * (nClass.equals(className) ||
  597. * nClass.startsWith(className.concat(", ")) ||
  598. * nClass.contains(", ".concat(className))));
  599. */
  600. return (nClass != null && nClass.contains(className));
  601. }
  602. }