View Javadoc
1   /*
2    * Copyright (c) Patrick Magauran 2018.
3    *   Licensed under the AGPLv3. All conditions of said license apply.
4    *       This file is part of ABOS.
5    *
6    *       ABOS is free software: you can redistribute it and/or modify
7    *       it under the terms of the GNU Affero General Public License as published by
8    *       the Free Software Foundation, either version 3 of the License, or
9    *       (at your option) any later version.
10   *
11   *       ABOS is distributed in the hope that it will be useful,
12   *       but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   *       GNU Affero General Public License for more details.
15   *
16   *       You should have received a copy of the GNU Affero General Public License
17   *       along with ABOS.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package Controllers;
21  
22  import Exceptions.AccessException;
23  import Launchers.AddGroup;
24  import Utilities.*;
25  import Workers.LoadUGYWorker;
26  import javafx.application.Platform;
27  import javafx.collections.FXCollections;
28  import javafx.collections.ObservableList;
29  import javafx.event.ActionEvent;
30  import javafx.fxml.FXML;
31  import javafx.fxml.FXMLLoader;
32  import javafx.geometry.Insets;
33  import javafx.scene.Node;
34  import javafx.scene.control.*;
35  import javafx.scene.control.cell.CheckBoxTreeCell;
36  import javafx.scene.control.cell.ComboBoxTableCell;
37  import javafx.scene.control.cell.PropertyValueFactory;
38  import javafx.scene.control.cell.TextFieldTableCell;
39  import javafx.scene.input.MouseEvent;
40  import javafx.scene.layout.*;
41  import javafx.stage.FileChooser;
42  import javafx.stage.Window;
43  import javafx.util.Pair;
44  import org.apache.commons.lang3.RandomStringUtils;
45  import org.w3c.dom.Attr;
46  import org.w3c.dom.Document;
47  import org.w3c.dom.Element;
48  import org.w3c.dom.NodeList;
49  
50  import javax.xml.parsers.DocumentBuilder;
51  import javax.xml.parsers.DocumentBuilderFactory;
52  import javax.xml.parsers.ParserConfigurationException;
53  import javax.xml.transform.*;
54  import javax.xml.transform.dom.DOMSource;
55  import javax.xml.transform.stream.StreamResult;
56  import java.io.*;
57  import java.math.BigDecimal;
58  import java.security.SecureRandom;
59  import java.sql.SQLException;
60  import java.time.LocalDate;
61  import java.util.*;
62  import java.util.regex.Pattern;
63  
64  //import org.w3c.dom.*;
65  
66  public class UsersGroupsAndYearsController {
67      @FXML
68      TabPane tabPane;
69      @FXML
70      TitledPane enabledUsersPane;
71      @FXML
72      TitledPane disabledUserPane;
73      @FXML
74      VBox groupVbox;
75      @FXML
76      VBox enabledUserVbox;
77      @FXML
78      ScrollPane enabledUsersScrollPane;
79      @FXML
80      TitledPane archivedUsersPane;
81      @FXML
82      VBox archivedUserVbox;
83      @FXML
84      ScrollPane archivedUsersScrollPane;
85      @FXML
86      ScrollPane disabledUsersScrollPane;
87      @FXML
88      ScrollPane groupScrollPane;
89      @FXML
90      VBox disabledUserVbox;
91      @FXML
92      TreeView<TreeItemPair<String, Pair<String, Object>>> summaryList;
93      @FXML
94      MenuButton userMenuBtn;
95      @FXML
96      SplitMenuButton addUserSplitBtn;
97      @FXML
98      MenuButton groupMenuBtn;
99      @FXML
100     Tab productsTab;
101     @FXML
102     Tab usersTab;
103     @FXML
104     Tab groupsTab;
105     Window parentWindow;
106     @FXML
107     private TableView<formattedProductProps> ProductTable;
108     @FXML
109     private TextField itemTb;
110     @FXML
111     private TextField sizeTb;
112     @FXML
113     private TextField rateTb;
114     @FXML
115     private TextField idTb;
116     //private final JDialog parent;
117     //UserName -> year -> UserObject
118     private Map<String, Map<String, User>> cachedUsers = new HashMap<>();
119     //year -> groups
120     private Map<String, ArrayList<Group>> cachedGroups = new HashMap<>();
121 
122     private Collection<Year.category> rowsCats = new ArrayList<Year.category>();
123     private ObservableList<String> categoriesTb = FXCollections.observableArrayList();
124     @FXML
125     private ComboBox<String> categoriesCmbx;
126     private ObservableList<formattedProductProps> data = FXCollections.observableArrayList();
127     private Map<User, ArrayList<String>> checkedUsers = new HashMap();
128     private Map<Group, ArrayList<String>> checkedGroups = new HashMap();
129     private Map<User, User.STATUS> selectedUsers = new HashMap<User, User.STATUS>();
130     private ArrayList<Group> selectedGroups = new ArrayList<>();
131     private ArrayList<CheckBox> userPaneCheckboxes = new ArrayList<>();
132     private ArrayList<CheckBox> groupPaneCheckboxes = new ArrayList<>();
133     private Map<User, Node> allUsers = new HashMap();
134     private Map<Group, Node> allGroups = new HashMap();
135     // private Map<String, ArrayList<String>> checkedFullName = new HashMap();
136 
137     private Map<User, Integer> groups = new HashMap<>();
138     private boolean isRightClick;
139     private String curYear = "";
140     public UsersGroupsAndYearsController() {
141 
142     }
143 
144     /**
145      * Initialize the contents of the frame.
146      */
147     public void initUsersGroupsAndYears(Window parWindow) throws AccessException {
148         parentWindow = parWindow;
149         fillTreeView(null);
150 
151         summaryList.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
152             if (event.isSecondaryButtonDown()) {
153                 isRightClick = true;
154 
155             }
156             if (event.getClickCount() == 2) {
157 
158                 Platform.runLater(() -> {
159                     TreeItemPair<String, Pair<String, Object>> newValue = summaryList.getSelectionModel().getSelectedItem().getValue();
160                     if (isRightClick) {
161                         //reset the flag
162                         isRightClick = false;
163                     } else if (newValue != null && !Objects.equals(newValue.getValue().getValue(), "RootNode")) {
164 
165                         switch (newValue.getValue().getKey()) {
166                             case "Year":
167                                 openYear(newValue.getKey());
168                                 break;
169                             case "Group":
170                                 String year = "";
171                                 if (summaryList.getSelectionModel().getSelectedItem().getParent().getValue().getValue().getKey().equals("Year")) {
172                                     year = summaryList.getSelectionModel().getSelectedItem().getParent().getValue().getKey();
173                                 } else if (summaryList.getSelectionModel().getSelectedItem().getParent().getParent().getValue().getValue().getKey().equals("Year")) {
174                                     year = summaryList.getSelectionModel().getSelectedItem().getParent().getParent().getValue().getKey();
175 
176                                 }
177                                 openGroup((Group) newValue.getValue().getValue(), year);
178                                 break;
179                             case "User":
180                                 year = "";
181                                 if (summaryList.getSelectionModel().getSelectedItem().getParent().getValue().getValue().getKey().equals("Year")) {
182                                     year = summaryList.getSelectionModel().getSelectedItem().getParent().getValue().getKey();
183                                 } else if (summaryList.getSelectionModel().getSelectedItem().getParent().getParent().getValue().getValue().getKey().equals("Year")) {
184                                     year = summaryList.getSelectionModel().getSelectedItem().getParent().getParent().getValue().getKey();
185 
186                                 }
187                                 openUser((User) newValue.getValue().getValue(), year);
188 
189                                 break;
190 
191 
192                         }
193                     }
194                 });
195             }
196         });
197 
198         summaryList.setCellFactory(p -> new TreeCellImpl());
199     }
200 
201     private void fillTreeView(actionCallback cb) {
202         //ProgressDialog progDial = new ProgressDialog();
203         ProgressForm progDial = new ProgressForm();
204 //Do check if new or not, send -1 as ID
205 
206         LoadUGYWorker loadWorker = new LoadUGYWorker(this);
207 
208         progDial.activateProgressBar(loadWorker);
209         loadWorker.setOnSucceeded(event -> {
210             summaryList.setRoot(loadWorker.getValue().getLeft());
211             cachedUsers = loadWorker.getValue().getMiddle();
212             cachedGroups = loadWorker.getValue().getRight();
213             progDial.getDialogStage().close();
214             if (cb != null) {
215                 cb.doAction();
216             }
217         });
218         loadWorker.setOnFailed(event -> {
219             progDial.getDialogStage().close();
220 
221             Throwable e = loadWorker.getException();
222 
223             if (e instanceof SQLException) {
224                 LogToFile.log((SQLException) e, Severity.SEVERE, CommonErrors.returnSqlMessage(((SQLException) loadWorker.getException())));
225 
226             }
227             if (e instanceof InterruptedException) {
228                 if (loadWorker.isCancelled()) {
229                     LogToFile.log((InterruptedException) e, Severity.FINE, "Load process canceled.");
230 
231                 }
232             }
233 
234 
235         });
236 
237 
238         progDial.getDialogStage().show();
239         new Thread(loadWorker).start();
240     }
241 
242     private void showTabs() {
243         productsTab.setDisable(false);
244         usersTab.setDisable(false);
245         groupsTab.setDisable(false);
246     }
247 
248     private void hideTabs() {
249         productsTab.setDisable(true);
250         usersTab.setDisable(true);
251         groupsTab.setDisable(true);
252 
253     }
254     private void openYear(String year) {
255         archivedUserVbox.getChildren().clear();
256         groupVbox.getChildren().clear();
257         disabledUserVbox.getChildren().clear();
258         enabledUserVbox.getChildren().clear();
259 
260         curYear = year;
261         Year yearObj = new Year(year);
262         //ArrayList<User> users = DbInt.getUsers();
263         allUsers.clear();
264         allGroups.clear();
265         selectedUsers.clear();
266         userPaneCheckboxes.clear();
267         checkedUsers.clear();
268         // checkedFullName.clear();
269         cachedUsers.forEach((key, value) -> {
270             User user = value.getOrDefault(year, null);
271             User.STATUS status;
272 
273             TitledPane userPane = new TitledPane();
274             if (user != null) {
275                 if (user.getYears().contains(year)) {
276                     status = User.STATUS.ENABLED;
277                     enabledUserVbox.getChildren().add(userPane);
278                 } else {
279                     status = User.STATUS.ARCHIVED;
280                     archivedUserVbox.getChildren().add(userPane);
281                 }
282             } else {
283                 status = User.STATUS.DISABLED;
284                 user = value.get("DB");
285                 disabledUserVbox.getChildren().add(userPane);
286 
287             }
288             CheckBox selectedCheckBox = new CheckBox(user.getFullName() + " (" + user.getUserName() + ")");
289             selectedCheckBox.setMinSize(CheckBox.USE_PREF_SIZE, CheckBox.USE_PREF_SIZE);
290             User finalUser = user;
291             userPaneCheckboxes.add(selectedCheckBox);
292             selectedCheckBox.selectedProperty().addListener((ob, oldVal, newVal) -> {
293                 if (newVal) {
294                     if (!selectedUsers.containsKey(finalUser)) {
295                         selectedUsers.put(finalUser, status);
296 
297                     }
298                 } else {
299                     if (selectedUsers.containsKey(finalUser)) {
300                         selectedUsers.remove(finalUser, status);
301 
302                     }
303                 }
304             });
305             //selectedCheckBox.setStyle("-fx-padding: 0px; -fx-border-color: red; -fx-border-width: 2px");
306             Button editButton = new Button("Edit");
307             editButton.setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);
308             editButton.setOnAction(event -> {
309                 editUser(finalUser);
310             });
311             Button deleteBtn = new Button("Delete");
312             deleteBtn.setOnAction(event -> {
313                 deleteUser(finalUser);
314             });
315             deleteBtn.getStyleClass().add("redBtn");
316             deleteBtn.setStyle("-fx-background-color: #ff4d4d");
317 
318             Pane spacer = new Pane();
319             HBox.setHgrow(spacer, Priority.ALWAYS);
320             spacer.setMinSize(10, 1);
321             spacer.setMaxWidth(Double.MAX_VALUE);
322             //BorderPane header = new BorderPane();
323             VBox headerRoot = new VBox();
324             headerRoot.setFillWidth(true);
325             HBox header = new HBox();
326             //header.setStyle("-fx-border-color: orange; -fx-border-width: 2px");
327             //header.setPrefWidth(HBox.USE_PREF_SIZE);
328             header.minWidthProperty().bind(userPane.widthProperty().subtract(75));
329             //userPane.prefWidthProperty().bindBidirectional(header.prefWidthProperty());
330             //userPane.minWidthProperty().bindBidirectional(header.minWidthProperty());
331             //selectedCheckBox.setAlignment(Pos.CENTER_LEFT);
332             //editButton.setAlignment(Pos.);
333             //header.setLeft(selectedCheckBox);
334             //header.setRight(editButton);
335             header.getChildren().setAll(selectedCheckBox, spacer, deleteBtn, editButton);
336             HBox.setHgrow(spacer, Priority.ALWAYS);
337             spacer.setMinSize(10, 1);
338             userPane.setText(user.getFullName() + " (" + user.getUserName() + ")");
339             headerRoot.getChildren().setAll(header);
340             userPane.setGraphic(header);
341             userPane.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
342             userPane.setExpanded(false);
343             //userPane.setPrefSize(Region.USE_COMPUTED_SIZE,700);
344             // userPane.setMinSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);
345 
346             userPane.getStyleClass().add("informationPane");
347 
348             VBox.setVgrow(userPane, Priority.ALWAYS);
349             FlowPane pane = new FlowPane();
350 
351 
352             ComboBox<TreeItemPair<String, Integer>> groupBox = new ComboBox<>();
353             TreeView<TreeItemPair<String, String>> yearTView;
354             CheckBoxTreeItem<TreeItemPair<String, String>> yearItem = new CheckBoxTreeItem<TreeItemPair<String, String>>(new TreeItemPair<>(year, ""));
355             User currentUser = user;
356             if (status != User.STATUS.DISABLED) {
357                 cachedGroups.get(year).forEach(group -> {
358                     CheckBoxTreeItem<TreeItemPair<String, String>> groupItem = new CheckBoxTreeItem<TreeItemPair<String, String>>(new TreeItemPair<>(group.getName(), ""));
359 
360                     group.getUsers().forEach(user2 -> {
361                         CheckBoxTreeItem<TreeItemPair<String, String>> userItem = createUserTreeItem(new TreeItemPair<>(user2.getFullName(), user2.getUserName()), currentUser);
362                         if (currentUser.getuManage().contains(user2.getUserName())) {
363                             userItem.setSelected(true);
364                             checkedUsers.computeIfPresent(currentUser, (k, v) -> {
365                                 if (!v.contains(user2.getUserName())) {
366 
367                                     v.add(user2.getUserName());
368                                 }
369                                 return v;
370                             });
371                             checkedUsers.computeIfAbsent(currentUser, k -> {
372                                 ArrayList<String> v = new ArrayList();
373                                 v.add(user2.getUserName());
374                                 return v;
375                             });
376                         /*checkedFullName.compute(year, (k, v) -> {
377                             ArrayList<String> vArray = new ArrayList();
378                             vArray.addAll(v);
379                             vArray.add(user2.getFullName());
380                             return vArray;
381                         });
382                        checkedFullName.computeIfAbsent(year, k -> {
383                             ArrayList<String> v = new ArrayList();
384                             v.add(user2.getFullName());
385                             return v;
386                         });*/
387                         }
388                         groupItem.getChildren().add(userItem);
389                     });
390                     yearItem.getChildren().add(groupItem);
391                     try {
392                         groupBox.getItems().add(new TreeItemPair<String, Integer>(group.getName(), group.getID()));
393                         if (currentUser.getGroupId() == group.getID()) {
394                             groupBox.getSelectionModel().selectLast();
395                         } else if (currentUser.getGroupId() == 0) {
396                             groupBox.getSelectionModel().selectFirst();
397 
398                         }
399 
400                     } catch (Group.GroupNotFoundException ignored) {
401                     }
402 
403                 });
404 
405                 yearTView = new TreeView(yearItem);
406                 yearTView.setPrefHeight(TreeView.USE_COMPUTED_SIZE);
407                 yearTView.setMinHeight(70);
408                 yearTView.setPrefWidth(TreeView.USE_COMPUTED_SIZE);
409                 yearTView.getStyleClass().add("lightTreeView");
410                 yearItem.setExpanded(true);
411                 yearTView.setCellFactory(CheckBoxTreeCell.forTreeView());
412                 yearTView.setFixedCellSize(35);
413 
414                 yearTView.expandedItemCountProperty().addListener((ob, oldVal, newVal) -> {
415                     Platform.runLater(() -> {
416                         yearTView.setPrefHeight(newVal.doubleValue() * (yearTView.getFixedCellSize()) + 10);
417                         yearTView.setMinHeight(newVal.doubleValue() * (yearTView.getFixedCellSize()) + 10);
418                         //yearTView.scrollTo(yearTView.getSelectionModel().getSelectedIndex());
419                         yearTView.refresh();
420                     });
421 
422                 });
423                 yearTView.refresh();
424                 groupBox.getSelectionModel().selectedItemProperty().addListener(observable -> {
425 
426                     groups.put(currentUser, groupBox.getSelectionModel().getSelectedItem().getValue());
427                 });
428                 VBox.setVgrow(yearTView, Priority.ALWAYS);
429                 VBox center = new VBox(10, new Label("Users to manage"), yearTView);
430                 center.setFillWidth(true);
431 
432                 BorderPane contents = new BorderPane(center, new HBox(10, new Label("Group to be a part of"), groupBox), null, null, null);
433                 contents.getStyleClass().add("containerPane");
434                 contents.setStyle("-fx-border: 0; -fx-border-insets: 0");
435                 contents.autosize();
436                 userPane.setContent(contents);
437 
438 
439                 groups.put(currentUser, groupBox.getSelectionModel().getSelectedItem().getValue());
440             }
441             allUsers.put(user, userPane);
442 
443         });
444 
445 
446         cachedGroups.get(year).forEach(group -> {
447             // TreeItem<TreeItemPair<String, String>> groupRoot = new TreeItem<TreeItemPair<String, String>>(new TreeItemPair<>(year, ""));
448 
449             TitledPane groupPane = new TitledPane();
450 
451             Label titleLabel = new Label(group.getName());
452             titleLabel.setMinSize(CheckBox.USE_PREF_SIZE, CheckBox.USE_PREF_SIZE);
453 
454             SplitMenuButton editButton = new SplitMenuButton();
455             editButton.setText("Edit");
456             editButton.setOnAction(event -> {
457                 editGroup(group);
458             });
459             Button deleteBtn = new Button("Delete");
460             deleteBtn.setOnAction(event -> {
461                 deleteGroup(group);
462 
463             });
464             deleteBtn.getStyleClass().add("redBtn");
465             deleteBtn.setStyle("-fx-background-color: #ff4d4d");
466 
467             MenuItem removeAllFromGroup = new MenuItem("Remove all From group");
468             removeAllFromGroup.setOnAction(event -> {
469                 checkedGroups.forEach((grp, Users) -> {
470                     Users.forEach(usrString -> {
471                         User usr = new User(usrString, year, true);
472                         usr.setGroupId(1);
473                         usr.updateYear(year);
474                     });
475                 });
476                 refresh(null);
477             });
478             editButton.getItems().add(removeAllFromGroup);
479             editButton.setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);
480 
481             Pane spacer = new Pane();
482             HBox.setHgrow(spacer, Priority.ALWAYS);
483             spacer.setMinSize(10, 1);
484             spacer.setMaxWidth(Double.MAX_VALUE);
485             //BorderPane header = new BorderPane();
486             VBox headerRoot = new VBox();
487             headerRoot.setFillWidth(true);
488             HBox header = new HBox();
489             //header.setStyle("-fx-border-color: orange; -fx-border-width: 2px");
490             //header.setPrefWidth(HBox.USE_PREF_SIZE);
491             header.minWidthProperty().bind(groupPane.widthProperty().subtract(75));
492             //userPane.prefWidthProperty().bindBidirectional(header.prefWidthProperty());
493             //userPane.minWidthProperty().bindBidirectional(header.minWidthProperty());
494             //selectedCheckBox.setAlignment(Pos.CENTER_LEFT);
495             //editButton.setAlignment(Pos.);
496             //header.setLeft(selectedCheckBox);
497             //header.setRight(editButton);
498             header.getChildren().setAll(titleLabel, spacer, deleteBtn, editButton);
499             HBox.setHgrow(spacer, Priority.ALWAYS);
500             spacer.setMinSize(10, 1);
501             headerRoot.getChildren().setAll(header);
502             groupPane.setGraphic(header);
503             groupPane.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
504             groupPane.setExpanded(false);
505             //userPane.setPrefSize(Region.USE_COMPUTED_SIZE,700);
506             // userPane.setMinSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);
507 
508             groupPane.getStyleClass().add("informationPane");
509 
510             groupVbox.getChildren().add(groupPane);
511 
512 
513             CheckBoxTreeItem<TreeItemPair<String, String>> groupItem = createGroupTreeItem(new TreeItemPair<>(group.getName(), ""), group);
514             group.getUsers().forEach(user2 -> {
515                 CheckBoxTreeItem<TreeItemPair<String, String>> userItem = createGroupTreeItem(new TreeItemPair<>(user2.getFullName(), user2.getUserName()), group);
516 
517                 groupItem.getChildren().add(userItem);
518             });
519             // groupRoot.getChildren().add(groupItem);
520 
521             TreeView<TreeItemPair<String, String>> groupTvView = new TreeView(groupItem);
522             groupTvView.setShowRoot(false);
523             groupTvView.setPrefHeight(TreeView.USE_COMPUTED_SIZE);
524             groupTvView.setMinHeight(70);
525             groupTvView.setPrefWidth(TreeView.USE_COMPUTED_SIZE);
526             groupTvView.getStyleClass().add("lightTreeView");
527             groupItem.setExpanded(true);
528             groupTvView.setCellFactory(CheckBoxTreeCell.forTreeView());
529             groupTvView.setFixedCellSize(35);
530 
531             groupTvView.expandedItemCountProperty().addListener((ob, oldVal, newVal) -> {
532                 Platform.runLater(() -> {
533                     groupTvView.setPrefHeight(newVal.doubleValue() * (groupTvView.getFixedCellSize()) + 10);
534                     groupTvView.setMinHeight(newVal.doubleValue() * (groupTvView.getFixedCellSize()) + 10);
535                     //yearTView.scrollTo(yearTView.getSelectionModel().getSelectedIndex());
536                     groupTvView.refresh();
537                 });
538 
539             });
540             groupTvView.refresh();
541 
542             groupPane.setContent(new ScrollPane(groupTvView));
543             allGroups.put(group, groupPane);
544 
545         });
546 
547         initProductsTab();
548         showTabs();
549     }
550 
551     private void openUser(User user, String year) {
552             openYear(year);
553 
554 
555         if (user.getYears().contains(year)) {
556 
557             TitledPane userPane = (TitledPane) allUsers.get(user);
558             userPane.setExpanded(true);
559             enabledUsersPane.setExpanded(true);
560             double vvalue = userPane.getLayoutX() / (userPane.getHeight() - enabledUsersScrollPane.getHeight());
561 
562             enabledUsersScrollPane.setVvalue(vvalue);
563         } else {
564             Year yearObj = new Year(year);
565             if (yearObj.getUsers().contains(user)) {
566                 TitledPane userPane = (TitledPane) allUsers.get(user);
567                 userPane.setExpanded(true);
568                 archivedUsersPane.setExpanded(true);
569                 double vvalue = userPane.getBoundsInParent().getMinY() / (userPane.getHeight() - archivedUsersScrollPane.getHeight());
570 
571                 archivedUsersScrollPane.setVvalue(vvalue);
572             } else {
573                 TitledPane userPane = (TitledPane) allUsers.get(user);
574                 userPane.setExpanded(true);
575                 disabledUserPane.setExpanded(true);
576                 double vvalue = userPane.getBoundsInParent().getMinY() / (userPane.getHeight() - disabledUsersScrollPane.getHeight());
577 
578                 disabledUsersScrollPane.setVvalue(vvalue);
579             }
580 
581 
582         }
583         tabPane.getSelectionModel().select(0);
584     }
585 
586     private void openGroup(Group group, String year) {
587             openYear(year);
588 
589 
590         TitledPane groupPane = (TitledPane) allGroups.get(group);
591         groupPane.setExpanded(true);
592         double vvalue = groupPane.getBoundsInParent().getMinY() / (groupPane.getHeight() - disabledUsersScrollPane.getHeight());
593 
594         groupScrollPane.setVvalue(vvalue);
595         tabPane.getSelectionModel().select(1);
596 
597     }
598 
599 
600     @FXML
601     private void addSelectedUsersToGroup(ActionEvent event) {
602 
603         Dialog<Group> dialog = new Dialog<>();
604         dialog.setTitle("Select Group");
605 
606 // Set the button types.
607         ButtonType login = new ButtonType("Group", ButtonBar.ButtonData.OK_DONE);
608         dialog.getDialogPane().getButtonTypes().addAll(login, ButtonType.CANCEL);
609 
610 // Create the username and password labels and fields.
611         GridPane grid = new GridPane();
612         grid.setHgap(10);
613         grid.setVgap(10);
614         grid.setPadding(new Insets(20, 150, 10, 10));
615 
616         ComboBox<Group> groupComboBox = new ComboBox<>();
617         groupComboBox.getItems().addAll(Group.getGroupCollection(curYear));
618 
619         grid.add(new Label("Group:"), 0, 0);
620         grid.add(groupComboBox, 1, 0);
621 
622 
623 // Enable/Disable login button depending on whether a username was entered.
624         javafx.scene.Node loginButton = dialog.getDialogPane().lookupButton(login);
625         loginButton.setDisable(true);
626 
627 // Do some validation (using the Java 8 lambda syntax).
628 
629         groupComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> loginButton.setDisable(newValue == null));
630 
631         dialog.getDialogPane().setContent(grid);
632 
633 // Request focus on the username field by default.
634         Platform.runLater(() -> groupComboBox.requestFocus());
635 
636 // Convert the result to a username-password-pair when the login button is clicked.
637         dialog.setResultConverter(dialogButton -> {
638             if (dialogButton == login) {
639                 return groupComboBox.getSelectionModel().getSelectedItem();
640             }
641             return null;
642         });
643 
644         Optional<Group> result = dialog.showAndWait();
645 
646         result.ifPresent(group -> {
647             Boolean alertDisabled = false;
648             selectedUsers.forEach((user, status) -> {
649                 if (status != User.STATUS.DISABLED) {
650                     try {
651                         user.setGroupId(group.getID());
652                     } catch (Group.GroupNotFoundException ignored) {
653                     }
654                     user.updateYear(curYear);
655                 }
656 
657             });
658             if (selectedUsers.containsValue(User.STATUS.DISABLED)) {
659                 Alert alert = new Alert(Alert.AlertType.INFORMATION);
660                 alert.setContentText("Operation applied. Disabled User(s) were skipped.");
661                 alert.show();
662             }
663             selectedUsers.clear();
664             unselectAllUserPanes();
665         });
666 
667 
668     }
669 
670     private void unselectAllUserPanes() {
671         userPaneCheckboxes.forEach(checkBox -> {
672             checkBox.setSelected(false);
673         });
674     }
675 
676     private void unselectAllGroupUsers() {
677 
678     }
679 
680     @FXML
681     private void disableSelectedUsers(ActionEvent event) {
682         Optional<Group> returnGroup = Optional.empty();
683         Dialog<String> dialog = new Dialog<>();
684         dialog.setTitle("REMOVE USERS FROM YEAR?");
685         dialog.setHeaderText("This will delete ALL customers and data associated with these users for the selected year.");
686 // Set the button types.
687         ButtonType addGrp = new ButtonType("Remove", ButtonBar.ButtonData.OK_DONE);
688         dialog.getDialogPane().getButtonTypes().addAll(addGrp, ButtonType.CANCEL);
689 
690 // Create the username and password labels and fields.
691         GridPane grid = new GridPane();
692         grid.setHgap(10);
693         grid.setVgap(10);
694         grid.setPadding(new Insets(20, 150, 10, 10));
695 
696         TextField verifyUNameTF = new TextField();
697         char[] possibleCharacters = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=").toCharArray();
698         String randomStr = RandomStringUtils.random(7, 0, possibleCharacters.length - 1, false, false, possibleCharacters, new SecureRandom());
699         grid.add(new Label("Please enter the verification code for confirmation:"), 0, 0);
700         grid.add(verifyUNameTF, 1, 0);
701         Label verificationCode = new Label(randomStr);
702         verificationCode.setStyle("-fx-font-size: 20px; -fx-font-weight: 600; -fx-color: black");
703         grid.add(new Label("Verification Code: "), 0, 1);
704         grid.add(verificationCode, 1, 1);
705 
706 
707 // Enable/Disable login button depending on whether a username was entered.
708         javafx.scene.Node deleteUserButton = dialog.getDialogPane().lookupButton(addGrp);
709         deleteUserButton.setDisable(true);
710         deleteUserButton.setStyle("fx-background-color: Red; fx-color: White");
711 // Do some validation (using the Java 8 lambda syntax).
712         verifyUNameTF.textProperty().addListener((observable, oldValue, newValue) -> {
713             if (Objects.equals(newValue, randomStr)) {
714                 deleteUserButton.setDisable(false);
715             } else {
716                 deleteUserButton.setDisable(true);
717             }
718         });
719 
720         dialog.getDialogPane().setContent(grid);
721 
722 // Request focus on the username field by default.
723         Platform.runLater(() -> verifyUNameTF.requestFocus());
724 
725 // Convert the result to a username-password-pair when the login button is clicked.
726         dialog.setResultConverter(dialogButton -> {
727             if (dialogButton == addGrp) {
728                 return verifyUNameTF.getText();
729             }
730             return null;
731         });
732 
733         Optional<String> result = dialog.showAndWait();
734         result.ifPresent(res -> {
735             if (res.equals(randomStr)) {
736                 selectedUsers.forEach((user, status) -> {
737                     user.deleteFromYear(curYear);
738 
739                 });
740                 hideTabs();
741                 openYear(curYear);
742             }
743         });
744     }
745 
746     @FXML
747     private void archiveSelectedUsers(ActionEvent event) {
748         Optional<Group> returnGroup = Optional.empty();
749         Dialog<String> dialog = new Dialog<>();
750         dialog.setTitle("ARCHIVE SELECTED USERS?");
751         dialog.setHeaderText("Selected users will no longer have access to the selected year.");
752 // Set the button types.
753         ButtonType addGrp = new ButtonType("Archive", ButtonBar.ButtonData.OK_DONE);
754         dialog.getDialogPane().getButtonTypes().addAll(addGrp, ButtonType.CANCEL);
755 
756 // Create the username and password labels and fields.
757         GridPane grid = new GridPane();
758         grid.setHgap(10);
759         grid.setVgap(10);
760         grid.setPadding(new Insets(20, 150, 10, 10));
761 
762         TextField verifyUNameTF = new TextField();
763         char[] possibleCharacters = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=").toCharArray();
764         String randomStr = RandomStringUtils.random(7, 0, possibleCharacters.length - 1, false, false, possibleCharacters, new SecureRandom());
765         grid.add(new Label("Please enter the verification code for confirmation:"), 0, 0);
766         grid.add(verifyUNameTF, 1, 0);
767         Label verificationCode = new Label(randomStr);
768         verificationCode.setStyle("-fx-font-size: 20px; -fx-font-weight: 600; -fx-color: black");
769         grid.add(new Label("Verification Code: "), 0, 1);
770         grid.add(verificationCode, 1, 1);
771 
772 
773 // Enable/Disable login button depending on whether a username was entered.
774         javafx.scene.Node deleteUserButton = dialog.getDialogPane().lookupButton(addGrp);
775         deleteUserButton.setDisable(true);
776         deleteUserButton.setStyle("fx-background-color: Red; fx-color: White");
777 // Do some validation (using the Java 8 lambda syntax).
778         verifyUNameTF.textProperty().addListener((observable, oldValue, newValue) -> {
779             if (Objects.equals(newValue, randomStr)) {
780                 deleteUserButton.setDisable(false);
781             } else {
782                 deleteUserButton.setDisable(true);
783             }
784         });
785 
786         dialog.getDialogPane().setContent(grid);
787 
788 // Request focus on the username field by default.
789         Platform.runLater(() -> verifyUNameTF.requestFocus());
790 
791 // Convert the result to a username-password-pair when the login button is clicked.
792         dialog.setResultConverter(dialogButton -> {
793             if (dialogButton == addGrp) {
794                 return verifyUNameTF.getText();
795             }
796             return null;
797         });
798 
799         Optional<String> result = dialog.showAndWait();
800         result.ifPresent(res -> {
801             if (res.equals(randomStr)) {
802                 selectedUsers.forEach((user, status) -> {
803                     Set<String> yrs = user.getYears();
804                     yrs.remove(curYear);
805                     user.setYears(yrs);
806                     user.updateYear(curYear);
807 
808                 });
809                 hideTabs();
810                 openYear(curYear);
811             }
812         });
813     }
814 
815     @FXML
816     private void enableSelectedUsers(ActionEvent event) {
817         selectedUsers.forEach((user, status) -> {
818             if (status == User.STATUS.DISABLED) {
819                 ArrayList<String> uMan = new ArrayList<>();
820                 uMan.add(user.getUserName());
821                 user.setuManage(uMan);
822                 Set<String> yrs = user.getYears();
823                 yrs.add(curYear);
824                 user.setYears(yrs);
825                 user.updateYear(curYear);
826             } else if (status == User.STATUS.ARCHIVED) {
827                 Set<String> yrs = user.getYears();
828                 yrs.add(curYear);
829                 user.setYears(yrs);
830                 user.updateYear(curYear);
831             }
832         });
833         hideTabs();
834         openYear(curYear);
835     }
836 
837     @FXML
838     private void addSelectedUsersToUser(ActionEvent event) {
839         Dialog<User> dialog = new Dialog<>();
840         dialog.setTitle("Select User as Manager");
841 
842 // Set the button types.
843         ButtonType login = new ButtonType("Set Manager", ButtonBar.ButtonData.OK_DONE);
844         dialog.getDialogPane().getButtonTypes().addAll(login, ButtonType.CANCEL);
845 
846 // Create the username and password labels and fields.
847         GridPane grid = new GridPane();
848         grid.setHgap(10);
849         grid.setVgap(10);
850         grid.setPadding(new Insets(20, 150, 10, 10));
851 
852         ComboBox<User> groupComboBox = new ComboBox<>();
853         groupComboBox.getItems().addAll(new Year(curYear).getUsers());
854 
855         grid.add(new Label("User:"), 0, 0);
856         grid.add(groupComboBox, 1, 0);
857 
858 
859 // Enable/Disable login button depending on whether a username was entered.
860         javafx.scene.Node loginButton = dialog.getDialogPane().lookupButton(login);
861         loginButton.setDisable(true);
862 
863 // Do some validation (using the Java 8 lambda syntax).
864 
865         groupComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> loginButton.setDisable(newValue == null));
866 
867         dialog.getDialogPane().setContent(grid);
868 
869 // Request focus on the username field by default.
870         Platform.runLater(() -> groupComboBox.requestFocus());
871 
872 // Convert the result to a username-password-pair when the login button is clicked.
873         dialog.setResultConverter(dialogButton -> {
874             if (dialogButton == login) {
875                 return groupComboBox.getSelectionModel().getSelectedItem();
876             }
877             return null;
878         });
879 
880         Optional<User> result = dialog.showAndWait();
881 
882         result.ifPresent(user -> {
883             Boolean alertDisabled = false;
884 
885             ArrayList<String> uManage = user.getuManage();
886             selectedUsers.forEach((selectedUser, status) -> {
887                 if (!uManage.contains(selectedUser.getUserName())) {
888                     uManage.add(selectedUser.getUserName());
889                 }
890             });
891             user.setuManage(uManage);
892 
893 
894             user.updateYear(curYear);
895 
896 
897             hideTabs();
898             openYear(curYear);
899         });
900     }
901 
902     @FXML
903     private void cancelUser(ActionEvent event) {
904 
905     }
906 
907     private void deleteUser(User user) {
908         Optional<Group> returnGroup = Optional.empty();
909         Dialog<String> dialog = new Dialog<>();
910         dialog.setTitle("REMOVE USER FROM YEAR?");
911         dialog.setHeaderText("This will delete ALL customers and data associated with this user for the selected year.");
912 // Set the button types.
913         ButtonType addGrp = new ButtonType("Remove", ButtonBar.ButtonData.OK_DONE);
914         dialog.getDialogPane().getButtonTypes().addAll(addGrp, ButtonType.CANCEL);
915 
916 // Create the username and password labels and fields.
917         GridPane grid = new GridPane();
918         grid.setHgap(10);
919         grid.setVgap(10);
920         grid.setPadding(new Insets(20, 150, 10, 10));
921 
922         TextField verifyUNameTF = new TextField();
923         char[] possibleCharacters = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=").toCharArray();
924         String randomStr = RandomStringUtils.random(7, 0, possibleCharacters.length - 1, false, false, possibleCharacters, new SecureRandom());
925         grid.add(new Label("Please enter the verification code for confirmation:"), 0, 0);
926         grid.add(verifyUNameTF, 1, 0);
927         Label verificationCode = new Label(randomStr);
928         verificationCode.setStyle("-fx-font-size: 20px; -fx-font-weight: 600; -fx-color: black");
929         grid.add(new Label("Verification Code: "), 0, 1);
930         grid.add(verificationCode, 1, 1);
931 
932 
933 // Enable/Disable login button depending on whether a username was entered.
934         javafx.scene.Node deleteUserButton = dialog.getDialogPane().lookupButton(addGrp);
935         deleteUserButton.setDisable(true);
936         deleteUserButton.setStyle("fx-background-color: Red; fx-color: White");
937 // Do some validation (using the Java 8 lambda syntax).
938         verifyUNameTF.textProperty().addListener((observable, oldValue, newValue) -> {
939             if (Objects.equals(newValue, randomStr)) {
940                 deleteUserButton.setDisable(false);
941             } else {
942                 deleteUserButton.setDisable(true);
943             }
944         });
945 
946         dialog.getDialogPane().setContent(grid);
947 
948 // Request focus on the username field by default.
949         Platform.runLater(() -> verifyUNameTF.requestFocus());
950 
951 // Convert the result to a username-password-pair when the login button is clicked.
952         dialog.setResultConverter(dialogButton -> {
953             if (dialogButton == addGrp) {
954                 return verifyUNameTF.getText();
955             }
956             return null;
957         });
958 
959         Optional<String> result = dialog.showAndWait();
960         result.ifPresent(res -> {
961             if (res.equals(randomStr)) {
962                 user.deleteFromYear(curYear);
963                 hideTabs();
964                 openYear(curYear);
965             }
966         });
967     }
968 
969     private void deleteGroup(Group group) {
970         Optional<Group> returnGroup = Optional.empty();
971         Dialog<String> dialog = new Dialog<>();
972         dialog.setTitle("Delete Group");
973         dialog.setHeaderText("This will move all the users to the ungrouped group.");
974 // Set the button types.
975         ButtonType addGrp = new ButtonType("Delete", ButtonBar.ButtonData.OK_DONE);
976         dialog.getDialogPane().getButtonTypes().addAll(addGrp, ButtonType.CANCEL);
977 
978 // Create the username and password labels and fields.
979         GridPane grid = new GridPane();
980         grid.setHgap(10);
981         grid.setVgap(10);
982         grid.setPadding(new Insets(20, 150, 10, 10));
983 
984         TextField verifyUNameTF = new TextField();
985         char[] possibleCharacters = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=").toCharArray();
986         String randomStr = RandomStringUtils.random(7, 0, possibleCharacters.length - 1, false, false, possibleCharacters, new SecureRandom());
987         grid.add(new Label("Please enter the verification code for confirmation:"), 0, 0);
988         grid.add(verifyUNameTF, 1, 0);
989         Label verificationCode = new Label(randomStr);
990         verificationCode.setStyle("-fx-font-size: 20px; -fx-font-weight: 600; -fx-color: black");
991         grid.add(new Label("Verification Code: "), 0, 1);
992         grid.add(verificationCode, 1, 1);
993 
994 
995 // Enable/Disable login button depending on whether a username was entered.
996         javafx.scene.Node deleteUserButton = dialog.getDialogPane().lookupButton(addGrp);
997         deleteUserButton.setDisable(true);
998         deleteUserButton.setStyle("fx-background-color: Red; fx-color: White");
999 // Do some validation (using the Java 8 lambda syntax).
1000         verifyUNameTF.textProperty().addListener((observable, oldValue, newValue) -> {
1001             if (Objects.equals(newValue, randomStr)) {
1002                 deleteUserButton.setDisable(false);
1003             } else {
1004                 deleteUserButton.setDisable(true);
1005             }
1006         });
1007 
1008         dialog.getDialogPane().setContent(grid);
1009 
1010 // Request focus on the username field by default.
1011         Platform.runLater(() -> verifyUNameTF.requestFocus());
1012 
1013 // Convert the result to a username-password-pair when the login button is clicked.
1014         dialog.setResultConverter(dialogButton -> {
1015             if (dialogButton == addGrp) {
1016                 return verifyUNameTF.getText();
1017             }
1018             return null;
1019         });
1020 
1021         Optional<String> result = dialog.showAndWait();
1022         result.ifPresent(res -> {
1023             if (res.equals(randomStr)) {
1024                 group.removeGroup();
1025                 refresh(null);
1026             }
1027         });
1028     }
1029 
1030     private void editUser(User user) {
1031         User oldUser = user;
1032         Dialog<Pair<Pair<String, Boolean>, Pair<String, String>>> dialog = new Dialog<>();
1033         dialog.setTitle("Edit User - " + user.toString());
1034 
1035 // Set the button types.
1036         ButtonType login = new ButtonType("Edit", ButtonBar.ButtonData.OK_DONE);
1037         dialog.getDialogPane().getButtonTypes().addAll(login, ButtonType.CANCEL);
1038 
1039 // Create the username and password labels and fields.
1040         GridPane grid = new GridPane();
1041         grid.setHgap(10);
1042         grid.setVgap(10);
1043         grid.setPadding(new Insets(20, 150, 10, 10));
1044 
1045         TextField userNameTextField = new TextField();
1046         userNameTextField.setText(user.getUserName());
1047         userNameTextField.setEditable(false);
1048         TextField fullNameField = new TextField();
1049         fullNameField.setText(user.getFullName());
1050         PasswordField passwordField = new PasswordField();
1051         passwordField.setPromptText("Password");
1052         CheckBox adminCheckBox = new CheckBox("Admin?");
1053         adminCheckBox.setSelected(user.isAdmin());
1054 
1055         grid.add(new Label("Username:"), 0, 0);
1056         grid.add(userNameTextField, 1, 0);
1057         grid.add(new Label("Full Name:"), 0, 1);
1058         grid.add(fullNameField, 1, 1);
1059         grid.add(new Label("Password:"), 0, 2);
1060         grid.add(passwordField, 1, 2);
1061         grid.add(adminCheckBox, 1, 3);
1062 
1063 
1064         dialog.getDialogPane().setContent(grid);
1065 
1066 // Request focus on the username field by default.
1067         Platform.runLater(() -> userNameTextField.requestFocus());
1068 
1069 // Convert the result to a username-password-pair when the login button is clicked.
1070         dialog.setResultConverter(dialogButton -> {
1071             if (dialogButton == login) {
1072                 return new Pair<Pair<String, Boolean>, Pair<String, String>>(new Pair<>(fullNameField.getText(), adminCheckBox.isSelected()), new Pair<>(userNameTextField.getText(), passwordField.getText()));
1073             }
1074             return null;
1075         });
1076 
1077         Optional<Pair<Pair<String, Boolean>, Pair<String, String>>> result = dialog.showAndWait();
1078 
1079         result.ifPresent(userInfo -> {
1080             Pattern p = Pattern.compile("[^a-zA-Z0-9]");
1081             String uName = userInfo.getValue().getKey();
1082             String pass = userInfo.getValue().getValue();
1083             String fName = userInfo.getKey().getKey();
1084             Boolean admin = userInfo.getKey().getValue();
1085             boolean hasSpecialChar = p.matcher(userInfo.getValue().getKey()).find();
1086             if (hasSpecialChar) {
1087                 Alert alert = new Alert(Alert.AlertType.WARNING);
1088                 alert.setTitle("");
1089                 alert.setHeaderText("You have entered an invalid character in the username");
1090                 alert.setContentText("Only Alphanumeric characters are aloud.");
1091                 alert.show();
1092             } else {
1093                 Set<String> years = new HashSet<>();
1094                 //Utilities.User.createUser(uName, pass, fullNameField.getText(), admin);
1095                 User.updateUser(uName, pass, fName, admin);
1096                 user.setFullName(fName);
1097                 user.setAdmin(admin);
1098                 user.getYears().forEach(year -> {
1099                     user.updateYear(year);
1100                 });
1101                 refresh(() -> {
1102                     openUser(user, curYear);
1103                 });
1104 
1105 
1106                 ArrayList<ArrayList<String>> yearUsers = new ArrayList<>();
1107 
1108 
1109             }
1110 
1111         });
1112 
1113 
1114     }
1115 
1116     private void editGroup(Group group) {
1117         AddGroup.addGroup(curYear, group.getName(), (grp) -> {
1118             refresh(() -> {openGroup(grp, curYear);});
1119 
1120         });
1121 
1122     }
1123 
1124     @FXML
1125     private void saveUsers(ActionEvent event) {
1126 
1127         ArrayList<ArrayList<String>> yearUsers = new ArrayList<>();
1128         checkedUsers.forEach((user, userManage) -> {
1129             ArrayList<String> usersManage = new ArrayList<>();
1130 
1131             userManage.forEach((uMan) -> {
1132                 if (!uMan.isEmpty()) {
1133                     usersManage.add(uMan);
1134 
1135                 }
1136             });
1137 
1138             if (!usersManage.isEmpty()) {
1139                 //years.add(year);
1140                 //if (newUser) {
1141                 user.setuManage(usersManage);
1142                 user.updateYear(curYear);
1143 
1144             }
1145         });
1146         groups.forEach((user, groupId) -> {
1147             user.setGroupId(groupId);
1148             user.updateYear(curYear);
1149         });
1150 
1151         refresh(null);
1152 
1153     }
1154 
1155     private void refresh(actionCallback cb) {
1156         hideTabs();
1157 
1158         fillTreeView(() -> {
1159             openYear(curYear);
1160             if (cb != null) {
1161                 cb.doAction();
1162             }
1163         });
1164     }
1165 
1166     @FXML
1167     private void addSingleUser(ActionEvent event) {
1168         Dialog<Pair<Pair<String, Boolean>, Pair<String, String>>> dialog = new Dialog<>();
1169         dialog.setTitle("Add User");
1170 
1171 // Set the button types.
1172         ButtonType login = new ButtonType("Add", ButtonBar.ButtonData.OK_DONE);
1173         dialog.getDialogPane().getButtonTypes().addAll(login, ButtonType.CANCEL);
1174 
1175 // Create the username and password labels and fields.
1176         GridPane grid = new GridPane();
1177         grid.setHgap(10);
1178         grid.setVgap(10);
1179         grid.setPadding(new Insets(20, 150, 10, 10));
1180 
1181         TextField userNameTextField = new TextField();
1182         userNameTextField.setPromptText("Username");
1183         TextField fullNameField = new TextField();
1184         fullNameField.setPromptText("Full Name");
1185         PasswordField passwordField = new PasswordField();
1186         passwordField.setPromptText("Password");
1187         CheckBox adminCheckBox = new CheckBox("Admin?");
1188 
1189         grid.add(new Label("Username:"), 0, 0);
1190         grid.add(userNameTextField, 1, 0);
1191         grid.add(new Label("Full Name:"), 0, 1);
1192         grid.add(fullNameField, 1, 1);
1193         grid.add(new Label("Password:"), 0, 2);
1194         grid.add(passwordField, 1, 2);
1195         grid.add(adminCheckBox, 1, 3);
1196 
1197 // Enable/Disable login button depending on whether a username was entered.
1198         javafx.scene.Node loginButton = dialog.getDialogPane().lookupButton(login);
1199         loginButton.setDisable(true);
1200 
1201 // Do some validation (using the Java 8 lambda syntax).
1202         userNameTextField.textProperty().addListener((observable, oldValue, newValue) -> loginButton.setDisable(newValue.trim().isEmpty()));
1203 
1204         dialog.getDialogPane().setContent(grid);
1205 
1206 // Request focus on the username field by default.
1207         Platform.runLater(() -> userNameTextField.requestFocus());
1208 
1209 // Convert the result to a username-password-pair when the login button is clicked.
1210         dialog.setResultConverter(dialogButton -> {
1211             if (dialogButton == login) {
1212                 return new Pair<Pair<String, Boolean>, Pair<String, String>>(new Pair<>(fullNameField.getText(), adminCheckBox.isSelected()), new Pair<>(userNameTextField.getText(), passwordField.getText()));
1213             }
1214             return null;
1215         });
1216 
1217         Optional<Pair<Pair<String, Boolean>, Pair<String, String>>> result = dialog.showAndWait();
1218 
1219         result.ifPresent(userInfo -> {
1220             Pattern p = Pattern.compile("[^a-zA-Z0-9]");
1221             String uName = userInfo.getValue().getKey();
1222             String pass = userInfo.getValue().getValue();
1223             String fName = userInfo.getKey().getKey();
1224             Boolean admin = userInfo.getKey().getValue();
1225             boolean hasSpecialChar = p.matcher(userInfo.getValue().getKey()).find();
1226             if (hasSpecialChar) {
1227                 Alert alert = new Alert(Alert.AlertType.WARNING);
1228                 alert.setTitle("");
1229                 alert.setHeaderText("You have entered an invalid character in the username");
1230                 alert.setContentText("Only Alphanumeric characters are aloud.");
1231                 alert.show();
1232             } else {
1233                 Set<String> years = new HashSet<>();
1234                 User user = User.createUser(uName, pass, fName, admin);
1235                 ArrayList<String> uMan = new ArrayList<>();
1236                 uMan.add(uName);
1237                 years.add(curYear);
1238                 user.setuManage(uMan);
1239                 user.setYears(years);
1240                 user.setGroupId(1);
1241                 user.updateYear(curYear);
1242                 refresh(() -> {
1243                     openUser(user, curYear);
1244 
1245                 });
1246                 //allUsersList.getItems().add(User.createUser(uName, pass, fName, admin));
1247      /*           } else {
1248                     Utilities.User.updateUser(uName, pass);
1249 
1250                 }*/
1251                 ArrayList<ArrayList<String>> yearUsers = new ArrayList<>();
1252 
1253 
1254             }
1255 
1256         });
1257 
1258     }
1259 
1260     @FXML
1261     private void addBulkUsers(ActionEvent event) {
1262 //TODO Implement
1263     }
1264 
1265     @FXML
1266     private void addBulkGroups(ActionEvent event) {
1267         //TODO Implement
1268     }
1269 
1270     @FXML
1271     private void addSingleGroup(ActionEvent event) {
1272         Group newGroup = AddGroup.addGroup(curYear, (grp -> {
1273             refresh(() -> {
1274                 openGroup(grp, curYear);
1275 
1276             });
1277         }));
1278 
1279 
1280 
1281     }
1282 
1283 
1284     private String getCurrentYear() {
1285         return curYear;
1286     }
1287 
1288     /*
1289      * PRODUCT TAB
1290      */
1291     private void initProductsTab() {
1292         boolean newYear = false;
1293         Year thisYear = new Year(getCurrentYear());
1294         //ProductTable = new TableView<>();
1295 
1296         categoriesCmbx.getItems().clear();
1297         categoriesTb.clear();
1298         categoriesTb.add("");
1299         String browse = "Add Category";
1300         rowsCats.clear();
1301         thisYear.getCategories().forEach((category) -> {
1302             categoriesTb.add(category.catName);
1303             rowsCats.add(category);
1304         });
1305 
1306         categoriesTb.add(browse);
1307         categoriesCmbx.getItems().setAll(categoriesTb);
1308         ProductTable.getColumns().clear();
1309         ProductTable.getItems().clear();
1310         String[][] columnNames = {{"ID", "productID"}, {"Item", "productName"}, {"Size", "productSize"}, {"Price/Item", "productUnitPriceString"}};
1311         //for (String[] column : columnNames) {
1312         {
1313             javafx.scene.control.TableColumn<formattedProductProps, String> idCol = new javafx.scene.control.TableColumn<>("ID");
1314             idCol.setCellValueFactory(new PropertyValueFactory<>("productID"));
1315             idCol.setCellFactory(TextFieldTableCell.forTableColumn());
1316             idCol.setOnEditCommit(t -> {
1317                 t.getRowValue().productID.set(t.getNewValue());
1318                 data.get(t.getTablePosition().getRow()).productID.set(t.getNewValue());
1319                 t.getTableView().refresh();
1320             });
1321             ProductTable.getColumns().add(idCol);
1322             //}
1323         }
1324         {
1325             javafx.scene.control.TableColumn<formattedProductProps, String> nameCol = new javafx.scene.control.TableColumn<>("Item");
1326             nameCol.setCellValueFactory(new PropertyValueFactory<>("productName"));
1327             nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
1328             nameCol.setOnEditCommit(t -> {
1329                 t.getRowValue().productName.set(t.getNewValue());
1330                 data.get(t.getTablePosition().getRow()).productName.set(t.getNewValue());
1331                 t.getTableView().refresh();
1332             });
1333             ProductTable.getColumns().add(nameCol);
1334             //}
1335         }
1336         {
1337             javafx.scene.control.TableColumn<formattedProductProps, String> sizeCol = new javafx.scene.control.TableColumn<>("Size");
1338             sizeCol.setCellValueFactory(new PropertyValueFactory<>("productSize"));
1339             sizeCol.setCellFactory(TextFieldTableCell.forTableColumn());
1340             sizeCol.setOnEditCommit(t -> {
1341                 t.getRowValue().productSize.set(t.getNewValue());
1342                 data.get(t.getTablePosition().getRow()).productSize.set(t.getNewValue());
1343                 t.getTableView().refresh();
1344             });
1345             ProductTable.getColumns().add(sizeCol);
1346             //}
1347         }
1348         {
1349             javafx.scene.control.TableColumn<formattedProductProps, String> unitCostCol = new javafx.scene.control.TableColumn<>("Price/Item");
1350             unitCostCol.setCellValueFactory(new PropertyValueFactory<>("productUnitPriceString"));
1351             unitCostCol.setCellFactory(TextFieldTableCell.forTableColumn());
1352             unitCostCol.setOnEditCommit(t -> {
1353                 try {
1354                     BigDecimal unitPrice = new BigDecimal(t.getNewValue());
1355                     t.getRowValue().productUnitPriceString.set(t.getNewValue());
1356                     t.getRowValue().productUnitPrice.set(new BigDecimal(t.getNewValue()));
1357                     data.get(t.getTablePosition().getRow()).productUnitPriceString.set(t.getNewValue());
1358                     data.get(t.getTablePosition().getRow()).productUnitPrice.set(new BigDecimal(t.getNewValue()));
1359                     t.getTableView().refresh();
1360                 } catch (NumberFormatException e) {
1361                     Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid number");
1362                     alert.setHeaderText("You have entered an invalid number.");
1363                     alert.show();
1364                     t.getRowValue().productUnitPriceString.set(t.getOldValue());
1365                 }
1366 
1367 
1368             });
1369             ProductTable.getColumns().add(unitCostCol);
1370             //}
1371         }
1372 
1373 
1374         javafx.scene.control.TableColumn<formattedProductProps, String> categoryColumn = new javafx.scene.control.TableColumn<>("Category");
1375         categoryColumn.setCellValueFactory(new PropertyValueFactory<>("productCategory"));
1376 
1377         categoryColumn.setCellFactory(ComboBoxTableCell.forTableColumn(categoriesTb));
1378 
1379         categoryColumn.setOnEditCommit(t -> {
1380             String newVal = catCmbxChanged(t.getNewValue());
1381 
1382             t.getRowValue().productCategory.set(newVal);
1383             data.get(t.getTablePosition().getRow()).productCategory.set(newVal);
1384 
1385         });
1386         ProductTable.getColumns().add(categoryColumn);
1387         // boolean updateDb = true;
1388         fillTable(getCurrentYear());
1389         productsTab.setDisable(false);
1390     }
1391 
1392 
1393     @FXML
1394     private void submit(ActionEvent event) {
1395 
1396 
1397         updateDb(getCurrentYear());
1398         Alert alert = new Alert(Alert.AlertType.INFORMATION, "Saved");
1399         alert.setHeaderText("Changes Saved.");
1400         alert.show();
1401 
1402     }
1403 
1404     @FXML
1405     private void tableFrmXML(ActionEvent event) {
1406         FileChooser chooser = new FileChooser();
1407         FileChooser.ExtensionFilter filter = new FileChooser.ExtensionFilter("XML files", "*.xml", "*.XML");
1408         chooser.getExtensionFilters().add(filter);
1409 
1410         chooser.setSelectedExtensionFilter(filter);
1411 //        logoLoc.setText(chooser.showOpenDialog(settings).getAbsolutePath());
1412         File xmlFile = chooser.showOpenDialog(parentWindow);
1413         if (xmlFile != null) {
1414             String path = xmlFile.getAbsolutePath();
1415             createTable(path);
1416         }
1417     }
1418 
1419     private void convert(String csvLoc, String xmlLoc) {
1420         List<String> headers = new ArrayList<>(5);
1421 
1422 
1423         File file = new File(csvLoc);
1424 
1425         try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
1426 
1427             DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
1428             DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
1429 
1430             Document newDoc = domBuilder.newDocument();
1431             // Root element
1432             Element rootElement = newDoc.createElement("LawnGarden");
1433             newDoc.appendChild(rootElement);
1434 
1435             int line = 0;
1436 
1437             String text;
1438             while ((text = reader.readLine()) != null) {
1439 
1440                 StringTokenizer st = new StringTokenizer(text, ";", false);
1441                 String[] rowValues = new String[st.countTokens()];
1442                 int index = 0;
1443                 while (st.hasMoreTokens()) {
1444 
1445                     String next = st.nextToken();
1446                     rowValues[index] = next;
1447                     index++;
1448 
1449                 }
1450 
1451                 //String[] rowValues = text.split(",");
1452 
1453                 if (line == 0) { // Header row
1454                     Collections.addAll(headers, rowValues);
1455                 } else { // Data row
1456                     Element rowElement = newDoc.createElement("Products");
1457                     rootElement.appendChild(rowElement);
1458                     Attr attr = newDoc.createAttribute("id");
1459                     attr.setValue(Integer.toString(line - 1));
1460                     rowElement.setAttributeNode(attr);
1461                     for (int col = 0; col < headers.size(); col++) {
1462                         String header = headers.get(col);
1463                         String value;
1464 
1465                         if (col < rowValues.length) {
1466                             value = rowValues[col].trim();
1467                         } else {
1468                             // ?? Default value
1469                             value = "";
1470                         }
1471 
1472                         Element curElement = newDoc.createElement(header);
1473                         curElement.appendChild(newDoc.createTextNode(value));
1474                         rowElement.appendChild(curElement);
1475                     }
1476                 }
1477                 line++;
1478             }
1479 
1480             OutputStreamWriter osw = null;
1481 
1482             try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
1483                 osw = new OutputStreamWriter(baos);
1484 
1485                 TransformerFactory tranFactory = TransformerFactory.newInstance();
1486                 Transformer aTransformer = tranFactory.newTransformer();
1487                 aTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
1488                 aTransformer.setOutputProperty(OutputKeys.METHOD, "xml");
1489                 //aTransformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
1490                 aTransformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
1491 
1492                 Source src = new DOMSource(newDoc);
1493                 Result result = new StreamResult(osw);
1494                 aTransformer.transform(src, result);
1495 
1496                 osw.flush();
1497                 //System.out.println(new String(baos.toByteArray()));
1498 
1499                 try (OutputStream outStream = new FileOutputStream(xmlLoc)) {// writing bytes in to byte output stream
1500 
1501                     baos.writeTo(outStream);
1502                 } catch (IOException e) {
1503                     LogToFile.log(e, Severity.SEVERE, "Error writing XML file. Please try again.");
1504                 }
1505 
1506 
1507             } catch (Exception exp) {
1508                 LogToFile.log(exp, Severity.SEVERE, "Error writing XML file. Please try again.");
1509             } finally {
1510                 try {
1511                     if (osw != null) {
1512                         osw.close();
1513                     }
1514                 } catch (IOException e) {
1515                     LogToFile.log(e, Severity.SEVERE, "Error closing file. Please try again.");
1516                 }
1517 
1518             }
1519         } catch (Exception e) {
1520             LogToFile.log(e, Severity.SEVERE, "Error reading CSV file. Ensure the path exists, and the software has permission to read it.");
1521         }
1522     }
1523 
1524     @FXML
1525     private void csvToXml(ActionEvent event) {
1526         // Create the custom dialog.
1527         Dialog<Pair<String, String>> dialog = new Dialog<>();
1528         dialog.setTitle("CSV to XML conversion");
1529 
1530 // Set the button types.
1531         ButtonType convertButtonType = new ButtonType("Convert", ButtonBar.ButtonData.OK_DONE);
1532         dialog.getDialogPane().getButtonTypes().addAll(convertButtonType, ButtonType.CANCEL);
1533 
1534 // Create the username and password labels and fields.
1535         GridPane grid = new GridPane();
1536         grid.setHgap(10);
1537         grid.setVgap(10);
1538         grid.setPadding(new Insets(20, 150, 10, 10));
1539 
1540         TextField csvLoc = new TextField();
1541         csvLoc.setPromptText("CSV file Location");
1542         TextField xmlLoc = new TextField();
1543         xmlLoc.setPromptText("XML Location");
1544         Button getCsvLoc = new Button("...");
1545         getCsvLoc.setOnAction(e -> {
1546             FileChooser chooser = new FileChooser();
1547             FileChooser.ExtensionFilter filter = new FileChooser.ExtensionFilter("CSV files", "*.csv", "*.CSV");
1548             chooser.getExtensionFilters().add(filter);
1549             chooser.setSelectedExtensionFilter(filter);
1550             File csv = chooser.showOpenDialog(grid.getScene().getWindow());
1551             if (csv != null) {
1552                 String path = csv.getAbsolutePath();
1553                 if (!path.toLowerCase().endsWith(".csv")) {
1554                     path += ".csv";
1555                 }
1556                 csvLoc.setText(path);
1557             }
1558         });
1559         Button getXmlLoc = new Button("...");
1560         getXmlLoc.setOnAction(e -> {
1561             FileChooser chooser = new FileChooser();
1562             FileChooser.ExtensionFilter filter = new FileChooser.ExtensionFilter("XML files", "*.xml", "*.XML");
1563             chooser.getExtensionFilters().add(filter);
1564             chooser.setSelectedExtensionFilter(filter);
1565             File XML = chooser.showSaveDialog(grid.getScene().getWindow());
1566             if (XML != null) {
1567                 String path = XML.getAbsolutePath();
1568                 if (!path.toLowerCase().endsWith(".xml")) {
1569                     path += ".xml";
1570                 }
1571                 xmlLoc.setText(path);
1572             }
1573         });
1574         grid.add(new Label("CSV file Location:"), 0, 0);
1575         grid.add(csvLoc, 1, 0);
1576         grid.add(getCsvLoc, 2, 0);
1577         grid.add(new Label("XML Location:"), 0, 1);
1578         grid.add(xmlLoc, 1, 1);
1579         grid.add(getXmlLoc, 2, 1);
1580 
1581 
1582 // Enable/Disable login button depending on whether a username was entered.
1583         javafx.scene.Node convertButton = dialog.getDialogPane().lookupButton(convertButtonType);
1584         convertButton.setDisable(true);
1585 
1586 // Do some validation (using the Java 8 lambda syntax).
1587         csvLoc.textProperty().addListener((observable, oldValue, newValue) -> convertButton.setDisable(newValue.trim().isEmpty()));
1588 
1589         dialog.getDialogPane().setContent(grid);
1590 
1591 // Request focus on the username field by default.
1592         Platform.runLater(() -> csvLoc.requestFocus());
1593 
1594 // Convert the result to a username-password-pair when the login button is clicked.
1595         dialog.setResultConverter(dialogButton -> {
1596             if (dialogButton == convertButtonType) {
1597                 return new Pair<>(csvLoc.getText(), xmlLoc.getText());
1598             }
1599             return null;
1600         });
1601 
1602         Optional<Pair<String, String>> result = dialog.showAndWait();
1603 
1604         result.ifPresent(fileLocations -> {
1605             convert(fileLocations.getKey(), fileLocations.getValue());
1606             createTable(fileLocations.getValue());
1607         });
1608 
1609 
1610 
1611 
1612 /*        CSV2XML csv = new CSV2XML(parent);
1613         String xmlFile = csv.getXML();
1614         if (!xmlFile.isEmpty()) {
1615             createTable(xmlFile);
1616         }*/
1617     }
1618 
1619     @FXML
1620     private void catCmbxChanged(ActionEvent event) {
1621         if (Objects.equals(categoriesCmbx.getSelectionModel().getSelectedItem(), "Add Category")) {
1622             Dialog<Pair<String, String>> dialog = new Dialog<>();
1623             dialog.setTitle("Add new category");
1624 
1625 // Set the button types.
1626             ButtonType addCat = new ButtonType("Add", ButtonBar.ButtonData.OK_DONE);
1627             dialog.getDialogPane().getButtonTypes().addAll(addCat, ButtonType.CANCEL);
1628 
1629 // Create the username and password labels and fields.
1630             GridPane grid = new GridPane();
1631             grid.setHgap(10);
1632             grid.setVgap(10);
1633             grid.setPadding(new Insets(20, 150, 10, 10));
1634 
1635             TextField catName = new TextField();
1636             catName.setPromptText("Category Name");
1637             DatePicker catDate = new DatePicker(LocalDate.now());
1638             catDate.setPromptText("Category Due Date");
1639 
1640             grid.add(new Label("Category Name:"), 0, 0);
1641             grid.add(catName, 1, 0);
1642             grid.add(new Label("Category Due Date:"), 0, 1);
1643             grid.add(catDate, 1, 1);
1644 
1645 
1646 // Enable/Disable login button depending on whether a username was entered.
1647             javafx.scene.Node addCatButton = dialog.getDialogPane().lookupButton(addCat);
1648             addCatButton.setDisable(true);
1649 
1650 // Do some validation (using the Java 8 lambda syntax).
1651             catName.textProperty().addListener((observable, oldValue, newValue) -> addCatButton.setDisable(newValue.trim().isEmpty()));
1652 
1653             dialog.getDialogPane().setContent(grid);
1654 
1655 // Request focus on the username field by default.
1656             Platform.runLater(() -> catName.requestFocus());
1657 
1658 // Convert the result to a username-password-pair when the login button is clicked.
1659             dialog.setResultConverter(dialogButton -> {
1660                 if (dialogButton == addCat) {
1661                     return new Pair<String, String>(catName.getText(), catDate.getValue().toString());
1662                 }
1663                 return null;
1664             });
1665 
1666             Optional<Pair<String, String>> result = dialog.showAndWait();
1667 
1668             result.ifPresent(category -> {
1669                 rowsCats.add(new Year.category(category.getKey(), category.getValue()));
1670                 Platform.runLater(() -> refreshCmbx());
1671 
1672             });
1673 
1674 
1675         }
1676 
1677     }
1678 
1679     private String catCmbxChanged(String newVal) {
1680         final Year.category newCat = new Year.category("", "");
1681         if (Objects.equals(newVal, "Add Category")) {
1682             Dialog<Pair<String, String>> dialog = new Dialog<>();
1683             dialog.setTitle("Add new category");
1684 
1685 // Set the button types.
1686             ButtonType addCat = new ButtonType("Add", ButtonBar.ButtonData.OK_DONE);
1687             dialog.getDialogPane().getButtonTypes().addAll(addCat, ButtonType.CANCEL);
1688 
1689 // Create the username and password labels and fields.
1690             GridPane grid = new GridPane();
1691             grid.setHgap(10);
1692             grid.setVgap(10);
1693             grid.setPadding(new Insets(20, 150, 10, 10));
1694 
1695             TextField catName = new TextField();
1696             catName.setPromptText("Category Name");
1697             DatePicker catDate = new DatePicker(LocalDate.now());
1698             catDate.setPromptText("Category Due Date");
1699 
1700             grid.add(new Label("Category Name:"), 0, 0);
1701             grid.add(catName, 1, 0);
1702             grid.add(new Label("Category Due Date:"), 0, 1);
1703             grid.add(catDate, 1, 1);
1704 
1705 
1706 // Enable/Disable login button depending on whether a username was entered.
1707             javafx.scene.Node addCatButton = dialog.getDialogPane().lookupButton(addCat);
1708             addCatButton.setDisable(true);
1709 
1710 // Do some validation (using the Java 8 lambda syntax).
1711             catName.textProperty().addListener((observable, oldValue, newValue) -> addCatButton.setDisable(newValue.trim().isEmpty()));
1712 
1713             dialog.getDialogPane().setContent(grid);
1714 
1715 // Request focus on the username field by default.
1716             Platform.runLater(() -> catName.requestFocus());
1717 
1718 // Convert the result to a username-password-pair when the login button is clicked.
1719             dialog.setResultConverter(dialogButton -> {
1720                 if (dialogButton == addCat) {
1721                     return new Pair<String, String>(catName.getText(), catDate.getValue().toString());
1722                 }
1723                 return null;
1724             });
1725 
1726             Optional<Pair<String, String>> result = dialog.showAndWait();
1727             result.ifPresent(category -> {
1728                 newCat.catName = category.getKey();
1729                 newCat.catDate = category.getValue();
1730                 rowsCats.add(newCat);
1731                 Platform.runLater(() -> refreshCmbx());
1732 
1733             });
1734 
1735 
1736         }
1737 
1738         return newCat.catName;
1739     }
1740 
1741     @FXML
1742     private void addBtnPressed(ActionEvent event) {
1743         int count = ProductTable.getItems().size() + 1;
1744         data.add(new formattedProductProps(0, idTb.getText(), itemTb.getText(), sizeTb.getText(), new BigDecimal(rateTb.getText()), categoriesCmbx.getSelectionModel().getSelectedItem(), 0, BigDecimal.ZERO));
1745         ProductTable.setItems(data);
1746     }
1747 
1748 
1749 
1750     private void refreshCmbx() {
1751         categoriesCmbx.getItems().clear();
1752         categoriesTb.clear();
1753         categoriesTb.add("");
1754         String browse = "Add Category";
1755 
1756         rowsCats.forEach(cat -> categoriesTb.add(cat.catName));
1757 
1758 
1759         categoriesTb.add(browse);
1760         categoriesCmbx.getItems().setAll(categoriesTb);
1761 
1762     }
1763 
1764     private void updateDb(String year) {
1765         Year yearToUpdate = new Year(year);
1766         yearToUpdate.updateDb(year, ProductTable.getItems(), rowsCats);
1767     }
1768 
1769     /**
1770      * Parses XML file to insert into products table on screen
1771      *
1772      * @param FLoc the location of the XML file
1773      */
1774     private void createTable(String FLoc) {
1775         try {
1776 
1777             File fXmlFile = new File(FLoc);
1778             DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
1779             DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
1780             Document doc = dBuilder.parse(fXmlFile);
1781 
1782             //optional, but recommended
1783             //read this - http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
1784             doc.getDocumentElement().normalize();
1785 
1786             //System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
1787             NodeList nListCats = doc.getElementsByTagName("Categories");
1788 
1789             // Collection<String[]> rowsCatsL = new ArrayList<>();
1790 
1791             for (int temp = 0; temp < nListCats.getLength(); temp++) {
1792 
1793                 org.w3c.dom.Node nNode = nListCats.item(temp);
1794 
1795 
1796                 if ((int) nNode.getNodeType() == (int) org.w3c.dom.Node.ELEMENT_NODE) {
1797 
1798                     Element eElement = (Element) nNode;
1799                     rowsCats.add(new Year.category(eElement.getElementsByTagName("CategoryName").item(0).getTextContent(), eElement.getElementsByTagName("CategoryDate").item(0).getTextContent()));
1800                 }
1801             }
1802             //rowsCats = rowsCatsL;
1803             NodeList nList = doc.getElementsByTagName("Products");
1804 
1805             Object[][] rows = new Object[nList.getLength()][5];
1806 
1807             for (int temp = 0; temp < nList.getLength(); temp++) {
1808 
1809                 org.w3c.dom.Node nNode = nList.item(temp);
1810 
1811 
1812                 if ((int) nNode.getNodeType() == (int) org.w3c.dom.Node.ELEMENT_NODE) {
1813 
1814                     Element eElement = (Element) nNode;
1815 
1816 
1817                     //String productID, String productName, String productSize, String productUnitPrice, String productCategory, int orderedQuantity, BigDecimal extendedCost
1818                     formattedProductProps prodProps = new formattedProductProps(0, eElement.getElementsByTagName(
1819                             "ProductID").item(0).getTextContent(),
1820                             eElement.getElementsByTagName("ProductName").item(0).getTextContent(),
1821                             eElement.getElementsByTagName("Size").item(0).getTextContent(),
1822                             new BigDecimal(eElement.getElementsByTagName("UnitCost").item(0).getTextContent()),
1823                             (eElement.getElementsByTagName("Category").item(0) != null) ? eElement.getElementsByTagName("Category").item(0).getTextContent() : "",
1824                             0,
1825                             BigDecimal.ZERO
1826                     );
1827                     data.add(prodProps);
1828                     ProductTable.setItems(data);
1829 
1830                 }
1831 
1832 
1833             }
1834         } catch (Exception e) {
1835             LogToFile.log(e, Severity.SEVERE, "Error Converting XML file to table. Please try again or contact support.");
1836         }
1837         refreshCmbx();
1838     }
1839 
1840     /**
1841      * Creates an XML file from the table
1842      *
1843      * @param SavePath Path to save the created XML file
1844      */
1845     private void createXML(String SavePath) {
1846         try {
1847             DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
1848             DocumentBuilder docBuilder;
1849 
1850             docBuilder = docFactory.newDocumentBuilder();
1851 
1852 
1853             // root elements
1854             Document doc = docBuilder.newDocument();
1855 
1856             Element rootElement = doc.createElement("LawnGarden");
1857             doc.appendChild(rootElement);
1858             Iterable<Year.category> caters;
1859             caters = rowsCats;
1860             int[] i = {0};
1861             //caters = getCategories(yearText.getText());
1862             caters.forEach(cat -> {
1863                         Element cats = doc.createElement("Categories");
1864                         rootElement.appendChild(cats);
1865                         Attr attr = doc.createAttribute("id");
1866                         attr.setValue(Integer.toString(i[0]));
1867                         cats.setAttributeNode(attr);
1868 
1869 
1870                         //CateName elements
1871                         Element ProductID = doc.createElement("CategoryName");
1872                         ProductID.appendChild(doc.createTextNode(cat.catName));
1873                         cats.appendChild(ProductID);
1874 
1875                         //CatDate elements
1876                         Element ProductName = doc.createElement("CategoryDate");
1877                         ProductName.appendChild(doc.createTextNode(cat.catDate));
1878                         cats.appendChild(ProductName);
1879                         i[0]++;
1880                     }
1881             );
1882 
1883             // staff elements
1884 
1885 
1886             // set attribute to staff element
1887             for (int i2 = 0; i2 < ProductTable.getItems().size(); i2++) {
1888 
1889                 Element staff = doc.createElement("Products");
1890                 rootElement.appendChild(staff);
1891                 Attr attr = doc.createAttribute("id");
1892                 attr.setValue(Integer.toString(i2));
1893                 staff.setAttributeNode(attr);
1894 
1895                 //ProductID elements
1896                 Element ProductID = doc.createElement("ProductID");
1897                 ProductID.appendChild(doc.createTextNode(ProductTable.getItems().get(i2).getProductID()));
1898                 staff.appendChild(ProductID);
1899 
1900                 // Prodcut Name elements
1901                 Element ProductName = doc.createElement("ProductName");
1902                 ProductName.appendChild(doc.createTextNode(ProductTable.getItems().get(i2).getProductName()));
1903                 staff.appendChild(ProductName);
1904 
1905                 // Unit COst elements
1906                 Element UnitCost = doc.createElement("UnitCost");
1907                 UnitCost.appendChild(doc.createTextNode(ProductTable.getItems().get(i2).getProductUnitPrice().toPlainString()));
1908                 staff.appendChild(UnitCost);
1909 
1910                 // Size elements
1911                 Element Size = doc.createElement("Size");
1912                 Size.appendChild(doc.createTextNode(ProductTable.getItems().get(i2).getProductSize()));
1913                 staff.appendChild(Size);
1914 
1915                 // Category elements
1916 
1917                 String cat = (ProductTable.getItems().get(i2).getProductCategory() != null) ? ProductTable.getItems().get(i2).getProductCategory() : "";
1918                 Element category = doc.createElement("Category");
1919                 category.appendChild(doc.createTextNode(cat));
1920                 staff.appendChild(category);
1921             }
1922 
1923 
1924             // write the content into xml file
1925             TransformerFactory transformerFactory = TransformerFactory.newInstance();
1926             Transformer transformer = transformerFactory.newTransformer();
1927             Source source = new DOMSource(doc);
1928             Result result = new StreamResult(new FileOutputStream(SavePath));
1929 
1930             // Output to console for testing
1931             // StreamResult result = new StreamResult(System.out);
1932 
1933             transformer.transform(source, result);
1934 
1935             //System.out.println("File saved!");
1936         } catch (ParserConfigurationException e) {
1937             LogToFile.log(e, Severity.SEVERE, "Error creating XML file: Parser error. Contact support.");
1938         } catch (TransformerException e) {
1939             LogToFile.log(e, Severity.SEVERE, "Error creating XML file: Parser Error. Contact support.");
1940         } catch (FileNotFoundException e) {
1941             LogToFile.log(e, Severity.SEVERE, "Error creating XML file: Error writing to file. Make sure the directory is readable by the software.");
1942         }
1943     }
1944 
1945     /**
1946      * Fills the table from a DB table
1947      */
1948     private void fillTable(String year) {
1949         Year yearInfo = new Year(year);
1950 
1951         formattedProduct[] productArray = yearInfo.getAllProducts();
1952         Object[][] rows = new Object[productArray.length][6];
1953         // data = FXCollections.observableArrayList();
1954 
1955         int i = 0;
1956         for (formattedProduct productOrder : productArray) {
1957             //String productID, String productName, String productSize, String productUnitPrice, String productCategory, int orderedQuantity, BigDecimal extendedCost
1958             formattedProductProps prodProps = new formattedProductProps(productOrder.productKey, productOrder.productID, productOrder.productName, productOrder.productSize, productOrder.productUnitPrice, productOrder.productCategory, productOrder.orderedQuantity, productOrder.extendedCost);
1959             data.add(prodProps);
1960             i++;
1961         }
1962 
1963         ProductTable.setItems(data);
1964 
1965     }
1966 
1967     @FXML
1968     private void tablefromDb(ActionEvent event) {
1969         fillTable(getCurrentYear());
1970     }
1971 
1972     @FXML
1973     private void xmlFromTable(ActionEvent event) {
1974         FileChooser chooser = new FileChooser();
1975         FileChooser.ExtensionFilter filter = new FileChooser.ExtensionFilter("XML files", "*.xml", "*.XML");
1976         chooser.getExtensionFilters().add(filter);
1977         chooser.setSelectedExtensionFilter(filter);
1978         File XML = chooser.showSaveDialog(parentWindow);
1979         if (XML != null) {
1980             String path = XML.getAbsolutePath();
1981             if (!path.toLowerCase().endsWith(".xml")) {
1982                 path += ".xml";
1983             }
1984             createXML(path);
1985         }
1986     }
1987 
1988     private String arrayToCSV(Collection<String> array) {
1989         final String[] ret = {""};
1990         array.forEach(value -> {
1991             if (!ret[0].isEmpty()) {
1992                 ret[0] = ret[0] + ", " + value;
1993             } else {
1994                 ret[0] = value;
1995             }
1996         });
1997         return ret[0];
1998     }
1999 
2000     private ContextMenu createContextMenu(TreeItem<TreeItemPair<String, Pair<String, Object>>> cell) {
2001         ContextMenu cm = new ContextMenu();
2002         ContextMenu cmContent = new ContextMenu();
2003         Pane newPane = null;
2004         FXMLLoader loader;
2005         Pane finalNewPane;
2006         // String tabTitle = "";
2007         if (cell != null && cell.getValue() != null && !Objects.equals(cell.getValue().getValue().getKey(), "RootNode")) {
2008             switch (cell.getValue().getValue().getKey()) {
2009                 case "Year":
2010                     cmContent = createContextMenuContent(
2011                             //Open
2012                             () -> {
2013                                 openYear(cell.getValue().getKey());
2014 
2015                             }, null,  //Open In New Window
2016                             null);
2017 
2018                     break;
2019                 case "Group":
2020                     cmContent = createContextMenuContent(
2021                             //Open
2022                             () -> {
2023                                 String year = "";
2024                                 if (cell.getParent().getValue().getValue().getKey().equals("Year")) {
2025                                     year = cell.getParent().getValue().getKey();
2026                                 } else if (cell.getParent().getParent().getValue().getValue().getKey().equals("Year")) {
2027                                     year = cell.getParent().getParent().getValue().getKey();
2028 
2029                                 }
2030                                 openGroup((Group) cell.getValue().getValue().getValue(), year);
2031 
2032                             }, null, null); //Open In New W
2033                     break;
2034                 case "User":
2035                     cmContent = createContextMenuContent(
2036                             //Open
2037                             () -> {
2038                                 String year = "";
2039                                 if (cell.getParent().getValue().getValue().getKey().equals("Year")) {
2040                                     year = cell.getParent().getValue().getKey();
2041                                 } else if (cell.getParent().getParent().getValue().getValue().getKey().equals("Year")) {
2042                                     year = cell.getParent().getParent().getValue().getKey();
2043 
2044                                 }
2045                                 openUser((User) cell.getValue().getValue().getValue(), year);
2046 
2047                             }, null, null);  //Open In New W
2048 
2049                     break;
2050 
2051 
2052             }
2053 
2054 
2055         }
2056         cm.getItems().addAll(cmContent.getItems());
2057         MenuItem refresh = new MenuItem("Refresh");
2058         refresh.setOnAction(event -> refresh(null));
2059         cm.getItems().add(refresh);
2060         // other menu items...
2061         return cm;
2062     }
2063 
2064     private <T> CheckBoxTreeItem<TreeItemPair<String, String>> createUserTreeItem(TreeItemPair<String, String> value, User user) {
2065 
2066         CheckBoxTreeItem<TreeItemPair<String, String>> item = new CheckBoxTreeItem<TreeItemPair<String, String>>(value);
2067         if (!value.getValue().isEmpty()) {
2068             item.selectedProperty().addListener((obs, wasChecked, isNowChecked) -> {
2069                 if (isNowChecked) {
2070                     checkedUsers.computeIfPresent(user, (k, v) -> {
2071                         if (!v.contains(value.getValue())) {
2072                             v.add(value.getValue());
2073                         }
2074                         return v;
2075                     });
2076                     checkedUsers.computeIfAbsent(user, k -> {
2077                         ArrayList<String> v = new ArrayList();
2078                         v.add(value.getValue());
2079                         return v;
2080                     });
2081                    /* checkedFullName.computeIfPresent(year, (k, v) -> {
2082                         v.add(value.getKey());
2083                         return v;
2084                     });
2085                     checkedFullName.computeIfAbsent(year, k -> {
2086                         ArrayList<String> v = new ArrayList();
2087                         v.add(value.getKey());
2088                         return v;
2089                     });*/
2090 
2091                 } else {
2092                     checkedUsers.compute(user, (k, v) -> {
2093                         v.remove(value.getValue());
2094                         return v;
2095                     });
2096   /*                checkedFullName.compute(year, (k, v) -> {
2097                         v.remove(value.getKey());
2098                         return v;
2099                     });*/
2100                 }
2101 
2102 
2103             });
2104         }
2105 
2106         return item;
2107     }
2108 
2109     private <T> CheckBoxTreeItem<TreeItemPair<String, String>> createGroupTreeItem(TreeItemPair<String, String> value, Group group) {
2110 
2111         CheckBoxTreeItem<TreeItemPair<String, String>> item = new CheckBoxTreeItem<TreeItemPair<String, String>>(value);
2112         if (!value.getValue().isEmpty()) {
2113             item.selectedProperty().addListener((obs, wasChecked, isNowChecked) -> {
2114                 if (isNowChecked) {
2115                     checkedGroups.computeIfPresent(group, (k, v) -> {
2116                         if (!v.contains(value.getValue())) {
2117                             v.add(value.getValue());
2118                         }
2119                         return v;
2120                     });
2121                     checkedGroups.computeIfAbsent(group, k -> {
2122                         ArrayList<String> v = new ArrayList();
2123                         v.add(value.getValue());
2124                         return v;
2125                     });
2126                    /* checkedFullName.computeIfPresent(year, (k, v) -> {
2127                         v.add(value.getKey());
2128                         return v;
2129                     });
2130                     checkedFullName.computeIfAbsent(year, k -> {
2131                         ArrayList<String> v = new ArrayList();
2132                         v.add(value.getKey());
2133                         return v;
2134                     });*/
2135 
2136                 } else {
2137                     checkedGroups.compute(group, (k, v) -> {
2138                         v.remove(value.getValue());
2139                         return v;
2140                     });
2141   /*                checkedFullName.compute(year, (k, v) -> {
2142                         v.remove(value.getKey());
2143                         return v;
2144                     });*/
2145                 }
2146 
2147 
2148             });
2149         }
2150 
2151         return item;
2152     }
2153 
2154     private ContextMenu createContextMenuContent(actionCallback open, actionCallback delete, actionCallback edit) {
2155         ContextMenu cm = new ContextMenu();
2156         if (open != null) {
2157             MenuItem openItem = new MenuItem("Open");
2158             openItem.setOnAction(event -> open.doAction());
2159             cm.getItems().add(openItem);
2160         }
2161 
2162 
2163         if (edit != null) {
2164             MenuItem openItem = new MenuItem("Edit");
2165             openItem.setOnAction(event -> edit.doAction());
2166             cm.getItems().add(openItem);
2167         }
2168         if (delete != null) {
2169             MenuItem openItem = new MenuItem("Delete");
2170             openItem.setOnAction(event -> delete.doAction());
2171             cm.getItems().add(openItem);
2172         }
2173         return cm;
2174     }
2175 
2176     interface actionCallback {
2177         void doAction();
2178     }
2179 
2180     abstract class AbstractTreeItem extends TreeItem {
2181 
2182         protected abstract ContextMenu getMenu();
2183     }
2184 
2185     public class contextTreeItem<K, V> extends AbstractTreeItem {
2186         // make class vars here like psswd
2187 
2188         public contextTreeItem(String key, Pair<String, String> value) {
2189             this.setValue(new TreeItemPair<>(key, value));
2190         }
2191 
2192         public contextTreeItem(String key, String value) {
2193             this.setValue(new TreeItemPair<>(key, new Pair<String, String>(value, "")));
2194         }
2195 
2196         public contextTreeItem(String key) {
2197             this.setValue(new TreeItemPair<>(key, null));
2198         }
2199 
2200         @Override
2201         public ContextMenu getMenu() {
2202 
2203             return createContextMenu(this);
2204         }
2205     }
2206 
2207     private final class TreeCellImpl<K, V> extends TreeCell<TreeItemPair<String, Pair<String, String>>> {
2208 
2209         @Override
2210         public void updateItem(TreeItemPair<String, Pair<String, String>> item, boolean empty) {
2211             super.updateItem(item, empty);
2212 
2213             if (empty) {
2214                 setText(null);
2215                 setGraphic(null);
2216             } else {
2217                 setText(getItem() == null ? "" : getItem().getKey());
2218                 setGraphic(getTreeItem().getGraphic());
2219                 setContextMenu(((AbstractTreeItem) getTreeItem()).getMenu());
2220             }
2221         }
2222     }
2223 }
2224