GraphManager.java 19 KB

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