PropertiesManager.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. package de.tu_darmstadt.informatik.tk.scopviz.ui;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.HashSet;
  5. import java.util.LinkedList;
  6. import java.util.Optional;
  7. import org.graphstream.graph.Element;
  8. import de.tu_darmstadt.informatik.tk.scopviz.debug.Debug;
  9. import de.tu_darmstadt.informatik.tk.scopviz.graphs.GraphHelper;
  10. import de.tu_darmstadt.informatik.tk.scopviz.graphs.GraphManager;
  11. import de.tu_darmstadt.informatik.tk.scopviz.graphs.MyEdge;
  12. import de.tu_darmstadt.informatik.tk.scopviz.graphs.MyNode;
  13. import de.tu_darmstadt.informatik.tk.scopviz.main.Layer;
  14. import de.tu_darmstadt.informatik.tk.scopviz.main.Main;
  15. import javafx.application.Platform;
  16. import javafx.beans.binding.Bindings;
  17. import javafx.collections.FXCollections;
  18. import javafx.collections.ObservableList;
  19. import javafx.event.EventHandler;
  20. import javafx.geometry.Insets;
  21. import javafx.scene.control.Alert;
  22. import javafx.scene.control.Alert.AlertType;
  23. import javafx.scene.control.ButtonBar.ButtonData;
  24. import javafx.scene.control.ButtonType;
  25. import javafx.scene.control.ChoiceBox;
  26. import javafx.scene.control.ContextMenu;
  27. import javafx.scene.control.Dialog;
  28. import javafx.scene.control.Label;
  29. import javafx.scene.control.MenuItem;
  30. import javafx.scene.control.TableColumn.CellEditEvent;
  31. import javafx.scene.control.TableRow;
  32. import javafx.scene.control.TableView;
  33. import javafx.scene.control.TextField;
  34. import javafx.scene.layout.GridPane;
  35. import javafx.util.Callback;
  36. /**
  37. * Manager for the Properties pane and its contents.
  38. *
  39. * @author Julian Ohl, Dominik Renkel
  40. * @version 1.6
  41. *
  42. */
  43. public final class PropertiesManager {
  44. /** Regex for detecting whether a String represent an Integer. */
  45. public static final String IS_INT = "^(-)?\\d+$";
  46. /** Regex for detecting whether a String represents a Boolean. */
  47. public static final String IS_BOOL = "^true$|^false$";
  48. /**
  49. * Regex for detecting whether a String represents a floating point number.
  50. */
  51. public static final String IS_FLOAT = "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$";
  52. /** The Table of Attributes. */
  53. private static TableView<KeyValuePair> properties;
  54. /** Flag whether the name has been set. */
  55. public static boolean nameSet;
  56. /** Flag whether the value has been set. */
  57. public static boolean valueSet;
  58. public static HashSet<TableRow<KeyValuePair>> tableRows = new HashSet<TableRow<KeyValuePair>>();
  59. /**
  60. * list for organizing items in the properties window in a specific order
  61. */
  62. private static LinkedList<String> itemOrderRules = new LinkedList<String>();
  63. /** hashmap for filtering items out of the properties window */
  64. private static HashMap<String, Integer> itemVisibilityRules = new HashMap<String, Integer>();
  65. /**
  66. * Private Constructor to prevent Instantiation.
  67. */
  68. private PropertiesManager() {
  69. }
  70. /**
  71. * Initializes the Manager by adding the list of properties to display into
  72. * the properties pane.
  73. *
  74. * @param propertiesInput
  75. * The list of properties to display
  76. */
  77. public static void initializeItems(TableView<KeyValuePair> propertiesInput) {
  78. properties = propertiesInput;
  79. setItemRules();
  80. }
  81. /**
  82. * setting up the rules for the items displayed in the properties window
  83. *
  84. * ****************************************************** add properties
  85. * here for grouping or filtering them out
  86. * ******************************************************
  87. */
  88. private static void setItemRules() {
  89. // setting the order for specific properties
  90. itemOrderRules.add("weight");
  91. itemOrderRules.add("ID");
  92. itemOrderRules.add("typeofNode");
  93. itemOrderRules.add("typeofDevice");
  94. itemOrderRules.add("x");
  95. itemOrderRules.add("y");
  96. itemOrderRules.add("lat");
  97. itemOrderRules.add("long");
  98. // properties, which shall be filtered out of the properties window
  99. itemVisibilityRules.put("layout.frozen", -1);
  100. //itemVisibilityRules.put("ui.style", -1);
  101. itemVisibilityRules.put("ui.j2dsk", -1);
  102. itemVisibilityRules.put("ui.clicked", -1);
  103. itemVisibilityRules.put("ui.map.selected", -1);
  104. itemVisibilityRules.put("xyz", -1);
  105. itemVisibilityRules.put("ui.pie-values", -1);
  106. // properties, which shall be filtered out of the properties window ,
  107. // only if debug is disabled
  108. itemVisibilityRules.put("mapping", -2);
  109. itemVisibilityRules.put("mapping-parent", -2);
  110. itemVisibilityRules.put("mapping-parent-id", -2);
  111. itemVisibilityRules.put("ui.class", -2);
  112. itemVisibilityRules.put("originalElement", -2);
  113. }
  114. /**
  115. * Update Properties of selected Node/Edge, if a any Property was changed.
  116. */
  117. public static final EventHandler<CellEditEvent<KeyValuePair, String>> setOnEditCommitHandler = new EventHandler<CellEditEvent<KeyValuePair, String>>() {
  118. @Override
  119. public void handle(CellEditEvent<KeyValuePair, String> t) {
  120. KeyValuePair editedPair = t.getTableView().getItems().get(t.getTablePosition().getRow());
  121. Object classType = editedPair.getClassType();
  122. String key = editedPair.getKey();
  123. // handling the problem when using his own names for properties
  124. // needed by graphstream
  125. // e.g. "ui.label" as "ID", might need an extra function/structure
  126. // if more of these are added
  127. if (key.equals("ID")) {
  128. key = "ui.label";
  129. }
  130. String oldValue = t.getOldValue();
  131. String newValue = t.getNewValue();
  132. Element selected = getSelected();
  133. // Type-Check the input
  134. if (classType.equals(Integer.class) && newValue.matches(IS_INT)) {
  135. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, key,
  136. newValue);
  137. selected.changeAttribute(key, Integer.valueOf(newValue));
  138. editedPair.setValue(newValue);
  139. Debug.out("Edited integer Attribute " + key);
  140. } else if (classType.equals(Boolean.class) && newValue.matches(IS_BOOL)) {
  141. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, key,
  142. newValue);
  143. selected.changeAttribute(key, Boolean.valueOf(newValue));
  144. editedPair.setValue(newValue);
  145. Debug.out("Edited boolean Attribute " + key);
  146. } else if (classType.equals(Float.class) && newValue.matches(IS_FLOAT)) {
  147. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, key,
  148. newValue);
  149. selected.changeAttribute(key, Float.valueOf(newValue));
  150. editedPair.setValue(newValue);
  151. Debug.out("Edited float Attribute " + key);
  152. } else if (classType.equals(Double.class) && newValue.matches(IS_FLOAT)) {
  153. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, key,
  154. newValue);
  155. selected.changeAttribute(key, Double.valueOf(newValue));
  156. editedPair.setValue(newValue);
  157. Debug.out("Edited double Attribute " + key);
  158. } else if (classType.equals(String.class)) {
  159. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, key,
  160. newValue);
  161. selected.changeAttribute(key, newValue);
  162. editedPair.setValue(newValue);
  163. Debug.out("Edited String Attribute " + key);
  164. if (key.equals("typeofNode")) {
  165. selected.changeAttribute("ui.class", newValue);
  166. }
  167. } else {
  168. editedPair.setValue(oldValue);
  169. t.getTableView().getItems().get(t.getTablePosition().getRow()).setKey(oldValue);
  170. setItemsProperties();
  171. Debug.out("WARNING: invalid input for this attribute type", 2);
  172. }
  173. // Unselect row after updating Property
  174. properties.getSelectionModel().clearSelection();
  175. }
  176. };
  177. /**
  178. * Callback to be executed when a right click occurs on the table.
  179. */
  180. public static Callback<TableView<KeyValuePair>, TableRow<KeyValuePair>> rightClickCallback = new Callback<TableView<KeyValuePair>, TableRow<KeyValuePair>>() {
  181. @Override
  182. public TableRow<KeyValuePair> call(TableView<KeyValuePair> tableView) {
  183. final TableRow<KeyValuePair> row = new TableRow<>();
  184. // ContextMenu on non empty rows (add & delete)
  185. final ContextMenu menuOnNonEmptyRows = new ContextMenu();
  186. final MenuItem addPropMenuItem = new MenuItem("Add..");
  187. final MenuItem deletePropMenuItem = new MenuItem("Delete");
  188. // ContextMenu on empty rows (only add)
  189. final ContextMenu menuOnEmptyRows = new ContextMenu();
  190. final MenuItem onlyAddPropMenuItem = new MenuItem("Add..");
  191. // add functionality
  192. onlyAddPropMenuItem.setOnAction((event) -> addPropFunctionality(null));
  193. addPropMenuItem.setOnAction((event) -> addPropFunctionality(null));
  194. // delete functionality
  195. deletePropMenuItem.setOnAction((event) -> {
  196. Debug.out("Remove Element");
  197. removeProperty(row.getItem());
  198. properties.getItems().remove(row.getItem());
  199. });
  200. // Disable MenuItem in symbol layer
  201. onlyAddPropMenuItem.disableProperty().bind(GraphDisplayManager.inSymbolLayerProperty());
  202. addPropMenuItem.disableProperty().bind(GraphDisplayManager.inSymbolLayerProperty());
  203. deletePropMenuItem.disableProperty().bind(GraphDisplayManager.inSymbolLayerProperty());
  204. // add MenuItem to ContextMenu
  205. menuOnEmptyRows.getItems().add(onlyAddPropMenuItem);
  206. menuOnNonEmptyRows.getItems().addAll(addPropMenuItem, deletePropMenuItem);
  207. // when empty row right-clicked open special menu (only add),
  208. // otherwise normal menu (add & delete)
  209. row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
  210. .then(menuOnNonEmptyRows).otherwise(menuOnEmptyRows));
  211. tableRows.add(row);
  212. return row;
  213. }
  214. };
  215. /**
  216. * Sets Property-TableView Elements to selected Node or Edge Properties.
  217. */
  218. public static void setItemsProperties() {
  219. String nid = Main.getInstance().getGraphManager().getSelectedNodeID();
  220. String eid = Main.getInstance().getGraphManager().getSelectedEdgeID();
  221. if (nid != null) {
  222. MyNode selectedNode = Main.getInstance().getGraphManager().getGraph().getNode(nid);
  223. showNewDataSet(selectedNode);
  224. } else if (eid != null) {
  225. MyEdge selectedEdge = Main.getInstance().getGraphManager().getGraph().getEdge(eid);
  226. showNewDataSet(selectedEdge);
  227. } else {
  228. return;
  229. }
  230. }
  231. /**
  232. * Add properties of selected Node or Edge to Properties TableView.
  233. *
  234. * @param selected
  235. * selected Node or Edge
  236. * @param newData
  237. */
  238. public static void showNewDataSet(Element selected) {
  239. ObservableList<KeyValuePair> newData = FXCollections.observableArrayList();
  240. if (selected == null) {
  241. properties.setItems(newData);
  242. return;
  243. }
  244. // fix for concurrentModification exception
  245. String[] temp = new String[0];
  246. temp = selected.getAttributeKeySet().toArray(temp);
  247. for (int i = 0; i < temp.length; i++) {
  248. String key = temp[i];
  249. if(key.startsWith("org.graphstream")){
  250. continue;
  251. }
  252. switch (key) {
  253. // filter out or change attributes added by graphstream that are of
  254. // no use to the user
  255. case "ui.label":
  256. if (selected instanceof MyNode) {
  257. Object actualAttribute = selected.getAttribute(key);
  258. // replace UI Label with ID"
  259. key = "ID";
  260. newData.add(0, new KeyValuePair(key, String.valueOf(actualAttribute), actualAttribute.getClass()));
  261. }
  262. break;
  263. case "weight":
  264. if (selected instanceof MyEdge
  265. && Layer.OPERATOR == Main.getInstance().getGraphManager().getGraph().getAttribute("layer")) {
  266. break;
  267. }
  268. Object actualAttribute = selected.getAttribute(key);
  269. if (actualAttribute != null) {
  270. newData.add(new KeyValuePair(key, String.valueOf(actualAttribute), actualAttribute.getClass()));
  271. }
  272. break;
  273. case "process-need":
  274. if (selected instanceof MyNode
  275. && Layer.UNDERLAY == Main.getInstance().getGraphManager().getGraph().getAttribute("layer")) {
  276. break;
  277. }
  278. actualAttribute = selected.getAttribute(key);
  279. if (actualAttribute != null) {
  280. newData.add(new KeyValuePair(key, String.valueOf(actualAttribute), actualAttribute.getClass()));
  281. }
  282. break;
  283. case "process-max":
  284. if (selected instanceof MyNode
  285. && Layer.OPERATOR == Main.getInstance().getGraphManager().getGraph().getAttribute("layer")) {
  286. break;
  287. }
  288. case "typeOfDevice":
  289. if (selected instanceof MyNode
  290. && Layer.OPERATOR == Main.getInstance().getGraphManager().getGraph().getAttribute("layer")) {
  291. break;
  292. }
  293. default:
  294. actualAttribute = selected.getAttribute(key);
  295. if (actualAttribute != null) {
  296. newData.add(new KeyValuePair(key, String.valueOf(actualAttribute), actualAttribute.getClass()));
  297. }
  298. break;
  299. }
  300. }
  301. properties.setItems(groupProperties(newData));
  302. }
  303. /**
  304. * Get the selected node or edge from the GraphManager.
  305. *
  306. * @return selected node or egde
  307. */
  308. private static Element getSelected() {
  309. GraphManager viz = Main.getInstance().getGraphManager();
  310. String nid = viz.getSelectedNodeID();
  311. String eid = viz.getSelectedEdgeID();
  312. if (nid != null) {
  313. return viz.getGraph().getNode(nid);
  314. } else if (eid != null) {
  315. return viz.getGraph().getEdge(eid);
  316. } else {
  317. return null;
  318. }
  319. }
  320. /**
  321. * Delete a given Pair from the current Node or Edge.
  322. *
  323. * @param pair
  324. * selectedProperty
  325. */
  326. private static void removeProperty(KeyValuePair pair) {
  327. Element selected = getSelected();
  328. selected.removeAttribute(pair.getKey());
  329. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, pair.getKey(), null);
  330. }
  331. /**
  332. * groups and filters a list of items according to the order and visibility
  333. * rules
  334. *
  335. * @param data
  336. * a list of property items
  337. * @return the data with the rules applied
  338. */
  339. private static ObservableList<KeyValuePair> groupProperties(ObservableList<KeyValuePair> data) {
  340. ObservableList<KeyValuePair> newData = FXCollections.observableArrayList();
  341. ;
  342. // adds all items in the order of the rules. Ordered items as an extra
  343. // list, removed from data
  344. for (String s : itemOrderRules) {
  345. for (int i = 0; i < data.size(); i++) {
  346. KeyValuePair kvp = data.get(i);
  347. if (kvp.getKey().equals(s)) {
  348. newData.add(kvp);
  349. data.remove(kvp);
  350. }
  351. }
  352. }
  353. // filters items according to the rules. Filters on the data without the
  354. // ordered items
  355. for (String key : itemVisibilityRules.keySet()) {
  356. for (int i = 0; i < data.size(); i++) {
  357. KeyValuePair kvp = data.get(i);
  358. if (kvp.getKey().equals(key)) {
  359. if (itemVisibilityRules.get(kvp.getKey()) == -1) {
  360. data.remove(kvp);
  361. }
  362. else if (itemVisibilityRules.get(kvp.getKey()) == -2) {
  363. if (!Debug.DEBUG_ENABLED) {
  364. data.remove(kvp);
  365. }
  366. }
  367. break;
  368. }
  369. }
  370. }
  371. // adds the filtered data without the ordered items behind the ordered
  372. // items
  373. newData.addAll(data);
  374. return newData;
  375. }
  376. /**
  377. * TODO Auslagern contextMenu add button functionality.
  378. */
  379. private static void addPropFunctionality(String preConfigPropName) {
  380. Debug.out("Add Element");
  381. // Create new Dialog
  382. Dialog<ArrayList<String>> addPropDialog = new Dialog<>();
  383. addPropDialog.setTitle("Add Property");
  384. addPropDialog.setHeaderText("Choose your Property Details");
  385. // Alert window -> when problems with input
  386. Alert alert = new Alert(AlertType.WARNING);
  387. alert.setTitle("Property-Type Alert");
  388. alert.setHeaderText("The selected Type doesnt fit the Input");
  389. alert.setContentText(null);
  390. ButtonType addButtonType = new ButtonType("Confirm", ButtonData.OK_DONE);
  391. addPropDialog.getDialogPane().getButtonTypes().addAll(addButtonType, ButtonType.CANCEL);
  392. // create grid
  393. GridPane grid = new GridPane();
  394. grid.setHgap(10);
  395. grid.setVgap(10);
  396. grid.setPadding(new Insets(20, 150, 10, 10));
  397. // create dialog elements
  398. TextField name = new TextField();
  399. name.setPromptText("Name");
  400. TextField value = new TextField();
  401. value.setPromptText("Value");
  402. ChoiceBox<String> type = new ChoiceBox<String>();
  403. type.setItems(FXCollections.observableArrayList("Integer", "Float", "String", "Boolean"));
  404. type.getSelectionModel().selectFirst();
  405. // position elements on grid
  406. grid.add(new Label("Property Name:"), 0, 0);
  407. grid.add(name, 1, 0);
  408. grid.add(new Label("Property Value:"), 0, 1);
  409. grid.add(value, 1, 1);
  410. grid.add(new Label("Property Type:"), 0, 2);
  411. grid.add(type, 1, 2);
  412. javafx.scene.Node confirmButton = addPropDialog.getDialogPane().lookupButton(addButtonType);
  413. confirmButton.setDisable(true);
  414. nameSet = false;
  415. valueSet = false;
  416. // show pre defined property name
  417. if (preConfigPropName != null) {
  418. name.setText(preConfigPropName);
  419. PropertiesManager.nameSet = true;
  420. }
  421. // hide confirm button, when textfields empty
  422. name.textProperty().addListener((observable, oldValue, newValue) -> {
  423. PropertiesManager.nameSet = true;
  424. if (newValue.trim().isEmpty()) {
  425. PropertiesManager.nameSet = false;
  426. confirmButton.setDisable(true);
  427. } else if (PropertiesManager.valueSet) {
  428. confirmButton.setDisable(false);
  429. }
  430. });
  431. value.textProperty().addListener((observable, oldValue, newValue) -> {
  432. PropertiesManager.valueSet = true;
  433. if (newValue.trim().isEmpty()) {
  434. PropertiesManager.valueSet = false;
  435. confirmButton.setDisable(true);
  436. } else if (PropertiesManager.nameSet) {
  437. confirmButton.setDisable(false);
  438. }
  439. });
  440. // set dialog
  441. addPropDialog.getDialogPane().setContent(grid);
  442. Platform.runLater(() -> name.requestFocus());
  443. // get new property values
  444. addPropDialog.setResultConverter(dialogButton -> {
  445. if (dialogButton == addButtonType) {
  446. ArrayList<String> tmp = new ArrayList<String>();
  447. tmp.add(name.getText());
  448. tmp.add(value.getText());
  449. tmp.add(type.getValue());
  450. return tmp;
  451. } else {
  452. return null;
  453. }
  454. });
  455. Optional<ArrayList<String>> result = addPropDialog.showAndWait();
  456. // create new Property
  457. result.ifPresent(t -> {
  458. System.out.println("Name: " + t.get(0) + ", Value: " + t.get(1) + ", Type: " + t.get(2));
  459. Element selected = getSelected();
  460. if (t.get(2).equals("Integer") && t.get(1).matches(IS_INT)) {
  461. selected.addAttribute(t.get(0), Integer.valueOf(t.get(1)));
  462. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, t.get(0),
  463. Integer.valueOf(t.get(1)));
  464. } else if (t.get(2).equals("Float") && t.get(1).matches(IS_FLOAT)) {
  465. selected.addAttribute(t.get(0), Float.valueOf(t.get(1)));
  466. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, t.get(0),
  467. Float.valueOf(t.get(1)));
  468. } else if (t.get(2).equals("String")) {
  469. selected.addAttribute(t.get(0), String.valueOf(t.get(1)));
  470. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, t.get(0),
  471. String.valueOf(t.get(1)));
  472. } else if (t.get(2).equals("Boolean") && t.get(1).matches(IS_BOOL)) {
  473. selected.addAttribute(t.get(0), Boolean.valueOf(t.get(1)));
  474. GraphHelper.propagateAttribute(Main.getInstance().getGraphManager().getGraph(), selected, t.get(0),
  475. Boolean.valueOf(t.get(1)));
  476. } else {
  477. // type doesnt fit input -> show alert and re-open property
  478. // creation window
  479. alert.showAndWait();
  480. addPropFunctionality(t.get(0));
  481. }
  482. showNewDataSet(selected);
  483. });
  484. }
  485. }