Thomas Ba 10 years ago
commit 36a73472ce

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/ext/commons-codec-1.10/commons-codec-1.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/ext/commons-validator-1.4.1/commons-validator-1.4.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="commons-codec" level="project" />
<orderEntry type="library" name="commons-validator" level="project" />
<orderEntry type="library" name="jfxrt" level="project" />
</component>
</module>

@ -0,0 +1,33 @@
package de.t_battermann.dhbw.todolist;
import java.io.File;
import java.io.IOException;
import java.util.Map;
/**
* Export the user data in a CSV file
*/
public class CSVHandler implements ExportHandler {
@Override
public void exportToFile(Map<String, User> users, File filename) throws IOException {
// TODO: implement method
}
@Override
public String exportToString(Map<String, User> users) {
// TODO: implement method
return null;
}
@Override
public Map<String, User> importFromFile(File filename) throws IOException, InvalidDataException {
// TODO: implement method
return null;
}
@Override
public Map<String, User> importFromString(String str) throws InvalidDataException {
// TODO: implement method
return null;
}
}

@ -0,0 +1,352 @@
package de.t_battermann.dhbw.todolist;
import com.sun.javafx.collections.ObservableListWrapper;
import com.sun.javafx.collections.ObservableSequentialListWrapper;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
public class Controller {
private Map<String,User> users = null;
private User currentUser = null;
private ObservableList<TodoList> todoLists = null;
private ObservableList<Todo> todos;
private Todo currentTodo = null;
private String filename = null;
private Map<String,Scene> scenes = new HashMap<>();
private Stage primaryStage;
public Controller(Stage primaryStage) {
this.primaryStage = primaryStage;
// Initialize needed scenes
try {
scenes.put("openFile", new Scene(FXMLLoader.load(getClass().getResource("openFile.fxml")),500,150));
scenes.put("login", new Scene(FXMLLoader.load(getClass().getResource("login.fxml")),500,350));
scenes.put("main", new Scene(FXMLLoader.load(getClass().getResource("main.fxml")),600,500));
scenes.put("saveAs", new Scene(FXMLLoader.load(getClass().getResource("saveAs.fxml")),500,150));
} catch (IOException e1) {
System.out.println("Could’t load a fxml file!");
e1.printStackTrace();
}
showLoadFileDialog();
}
/**
* Initialize new empty
*/
public void initEmpty() {
this.users = new TreeMap<>();
}
public void initFromFile(String filename) throws IOException, InvalidDataException {
File f = new File(filename);
if ( !f.isFile() || f.isDirectory() || !f.canRead()) {
throw new IOException();
}
ExportHandler e;
if ( filename.endsWith(".csv") ) {
e = new CSVHandler();
}else{
e = new XMLHandler();
}
this.users = e.importFromFile(new File(filename));
this.filename = filename;
}
public boolean export(String filename) {
if ( filename != null) {
File f = new File(filename);
if ( f.isDirectory() ) {
this.updateStatusLine("Couldn’t write to file '" + filename + "'");
return false;
}
ExportHandler e;
if ( filename.endsWith(".csv") ) {
e = new CSVHandler();
}else {
e = new XMLHandler();
}
try {
e.exportToFile(this.users, f);
} catch (IOException e1) {
this.updateStatusLine("Could’t write to file!");
e1.printStackTrace();
return false;
}
return true;
}
return false;
}
public void showLoadFileDialog() {
// log out (doesn't do anything if not logged in)
this.users = null;
this.currentUser = null;
this.todoLists = null;
this.todos = null;
this.filename = null;
// show dialog
primaryStage.setTitle("TodoList :: Open database");
primaryStage.setScene(scenes.get("openFile"));
primaryStage.show();
// register event handlers
Button b = (Button) primaryStage.getScene().lookup("#openFileButton");
b.setOnMouseReleased(event -> {
Node n = primaryStage.getScene().lookup("#openFilePath");
if ( n != null && n instanceof TextField ) {
try {
this.initFromFile(((TextField) n).getText());
this.showLoginDialog();
} catch (InvalidDataException | IOException e1) {
System.out.println("Can’t read file '" + this.filename + "'");
e1.printStackTrace();
}
}else{
System.out.println("Didn’t find #openFilePath!");
}
});
b = (Button) primaryStage.getScene().lookup("#openFileNew");
b.setOnMouseReleased(event -> {
this.initEmpty();
this.showLoginDialog();
});
}
private void showLoginDialog() {
primaryStage.setTitle("TodoList :: Log in");
primaryStage.setScene(scenes.get("login"));
TitledPane a = (TitledPane) primaryStage.getScene().lookup(users.isEmpty() ? "#createNewUserPane" : "#loginPane");
if ( a != null ) {
a.setExpanded(true);
}
// Log in
Node n = primaryStage.getScene().lookup("#loginButton");
if ( n != null && n instanceof Button) {
n.setOnMouseReleased(event -> {
Label l = (Label) primaryStage.getScene().lookup("#labelHints");
String name = null;
String pass = null;
Node m = primaryStage.getScene().lookup("#loginUsername");
if (m != null && m instanceof TextField) {
name = ((TextField) m).getText();
} else {
System.out.println("'#loginUsername' not found!");
return;
}
m = primaryStage.getScene().lookup("#loginPassword");
if (m != null && m instanceof PasswordField) {
pass = ((PasswordField) m).getText();
}
if (this.users.containsKey(name)) {
this.currentUser = this.users.get(name);
if (this.currentUser.checkLoginData(pass)) {
this.todoLists = new ObservableListWrapper<>(currentUser.getTodoLists());
this.showMainWindow();
}else{
this.currentUser = null;
l.setText("Invalid credentials!");
}
}else{
l.setText("Invalid credentials! 1");
}
});
}else{
System.out.println("'#loginButton' not found!");
}
n = primaryStage.getScene().lookup("#registerButton");
if ( n != null && n instanceof Button) {
n.setOnMouseReleased(event -> {
Label l = (Label) primaryStage.getScene().lookup("#labelHintsCreateNewUser");
// username
String username;
Node no = primaryStage.getScene().lookup("#registerUsername");
if ( no != null && no instanceof TextField && User.checkUsername(((TextField) no).getText())) {
username = ((TextField) no).getText();
}else{
l.setText("Invalid username! (Regex: [a-zA-Z0-9_-]{3,})");
return;
}
if ( users.containsKey(username)) {
l.setText("Username already taken!");
return;
}
// password
String password;
no = primaryStage.getScene().lookup("#registerPassword");
if( no != null && no instanceof PasswordField && User.checkPassword(((PasswordField) no).getText())) {
password = ((PasswordField) no).getText();
no = primaryStage.getScene().lookup("#registerPasswordRepeat");
if( no == null || !(no instanceof PasswordField) || !password.equals(((PasswordField) no).getText())) {
l.setText("The passwords didn’t match!");
}
}else{
l.setText("Password must be longer than 6 chars! And must contain numbers, lower and upper chars!");
return;
}
// eMail
String email;
no = primaryStage.getScene().lookup("#registerEmail");
if( no != null && no instanceof TextField && User.checkEmail( ((TextField) no).getText() ) ) {
email = ((TextField) no).getText();
no = primaryStage.getScene().lookup("#registerEmailRepeat");
if( no == null || !(no instanceof TextField) || !email.equals(((TextField) no).getText())) {
l.setText("The eMail addresses didn’t match!");
}
}else{
l.setText("No valid eMail address given!");
return;
}
User nu = new User(username,password);
nu.setEmail(email);
currentUser = nu;
users.put(username, nu);
this.todoLists = new ObservableSequentialListWrapper<>(currentUser.getTodoLists());
});
}else{
System.out.println("'#registerButton' not found!");
}
}
private void showMainWindow() {
primaryStage.setTitle("TodoList :: " + currentUser.getUsername() + " > Default");
primaryStage.setScene(scenes.get("main"));
Node n = primaryStage.getScene().lookup("#todoLists");
if ( n != null && n instanceof ListView) {
ListView<TodoList> lv = (ListView<TodoList>) n;
lv.setItems(this.todoLists);
lv.scrollTo(currentUser.getTodoList("Default"));
lv.getSelectionModel().selectedIndexProperty().addListener(event -> {
this.updateSelectedTodoList();
});
}
this.todos = new ObservableListWrapper<>(currentUser.getTodoList("Default").getTodos());
n = primaryStage.getScene().lookup("#todos");
if ( n != null && n instanceof ListView) {
ListView<Todo> lv = (ListView<Todo>) n;
lv.setItems(this.todos);
lv.getSelectionModel().selectedIndexProperty().addListener(event -> {
this.updateSelectedTodo();
});
}
n = primaryStage.getScene().lookup("#menuSave");
if ( n != null && n instanceof Button) {
n.setOnMouseReleased(event -> {
if ( this.filename != null) {
this.export(this.filename);
}else{
this.showSaveAs();
}
});
}
n = primaryStage.getScene().lookup("#menuSaveAs");
if ( n != null && n instanceof Button ) {
n.setOnMouseReleased(event -> this.showSaveAs());
}
n = primaryStage.getScene().lookup("#menuClose");
if ( n != null && n instanceof Button ) {
n.setOnMouseReleased(event -> Platform.exit());
}
this.primaryStage.setOnCloseRequest(event -> Platform.exit());
}
private void updateSelectedTodoList() {
Node n = primaryStage.getScene().lookup("#todoLists");
if ( n == null || !(n instanceof ListView) ) {
return;
}
ListView l = (ListView) n;
n = primaryStage.getScene().lookup("#todos");
if ( n == null || !(n instanceof ListView)) {
return;
}
ListView<Todo> lt = (ListView) n;
if ( l.getSelectionModel().getSelectedItem() != null && l.getSelectionModel().getSelectedItem() instanceof TodoList ) {
TodoList t = (TodoList) l.getSelectionModel().getSelectedItem();
this.todos = new ObservableListWrapper<>(t.getTodos());
lt.setItems(this.todos);
lt.getSelectionModel().select(0);
}
updateSelectedTodo();
}
private void updateSelectedTodo() {
Node n = primaryStage.getScene().lookup("#todos");
if ( n != null && n instanceof ListView) {
ListView<Todo> lv = (ListView<Todo>) n;
if(lv.getSelectionModel().getSelectedItem() != null) {
this.currentTodo = lv.getSelectionModel().getSelectedItem();
}
}
// title
n = primaryStage.getScene().lookup("#todoDetailTitle");
if ( n == null || !(n instanceof TextField))
return;
((TextField) n).setText( this.currentTodo.getTitle() );
// comment
n = primaryStage.getScene().lookup("#todoDetailDescription");
if ( n == null || !(n instanceof TextArea) )
return;
((TextArea) n).setText( this.currentTodo.getComment() );
// if dueDate set:
n = primaryStage.getScene().lookup("#todoDetailDueDate");
if ( n == null || !(n instanceof CheckBox) )
return;
boolean dueDate = this.currentTodo.getDueDate() != null;
((CheckBox) n).setSelected(dueDate);
// datePicker
n = primaryStage.getScene().lookup("#todoDetailDate");
if( n == null || !(n instanceof DatePicker))
return;
if(dueDate) {
((DatePicker) n).setValue( LocalDateTime.ofInstant(this.currentTodo.getDueDate().getTime().toInstant(), ZoneId.systemDefault()).toLocalDate() );
}
// time
n = primaryStage.getScene().lookup("#todoDetailTime");
if ( n == null || !(n instanceof TextField))
return;
((TextField) n).setText(this.currentTodo.getTime());
}
private void updateStatusLine(String text) {
Node n = primaryStage.getScene().lookup("#statusLine");
if ( n != null && n instanceof Label) {
((Label) n).setText(text);
}else{
System.out.println("Couldn’t find status line!");
}
}
private void showSaveAs() {
// TODO
Stage save = new Stage();
save.setScene(scenes.get("saveAs"));
save.setTitle("Save as ...");
save.show();
Button s = (Button) save.getScene().lookup("#save");
if ( s != null )
s.setOnMouseReleased(event -> {
TextField f = (TextField)save.getScene().lookup("#filename");
if ( f != null) {
File file = new File(f.getText());
if ( !file.isDirectory() ) {
if ( this.filename == null)
this.filename = f.getText();
if(this.export(f.getText()))
save.close();
}else{
this.updateStatusLine("Can’t write to file!");
}
}
});
}
}

@ -0,0 +1,42 @@
package de.t_battermann.dhbw.todolist;
import java.io.File;
import java.io.IOException;
import java.util.Map;
/**
* Interface for exporting/saving the data
*/
public interface ExportHandler {
/**
* Export to file.
*
* @param users A Map containing the users
* @param filename Path to the file the data should be saved to
*/
void exportToFile(Map<String, User> users, File filename) throws IOException;
/**
* Export to string.
*
* @param users the users
* @return A String containing the data
*/
String exportToString(Map<String, User> users);
/**
* Import from file.
*
* @param filename Path to the saved data
* @return A Map containing the Users, username as index
*/
Map<String, User> importFromFile(File filename) throws IOException, InvalidDataException;
/**
* Import from string.
*
* @param str A String containing the Data
* @return A Map containing the User, username as index
*/
Map<String, User> importFromString(String str) throws InvalidDataException;
}

@ -0,0 +1,13 @@
package de.t_battermann.dhbw.todolist;
/**
* This exception is thrown when the imported data is not as expected.
*/
public class InvalidDataException extends Exception {
public InvalidDataException() {
}
public InvalidDataException(String message) {
super(message);
}
}

@ -0,0 +1,17 @@
package de.t_battermann.dhbw.todolist;
import javafx.application.Application;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
new Controller(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}

@ -0,0 +1,165 @@
package de.t_battermann.dhbw.todolist;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.UUID;
/**
* This class represents a todo item containing all the data.
*/
public class Todo {
private String uuid;
private String title = "No title";
private boolean done = false;
private boolean prio = false;
private String comment = "";
private Calendar dueDate = null;
/**
* Instantiates a new empty Todo item.
*/
public Todo() {
this.uuid = UUID.randomUUID().toString();
}
/**
* Instantiates a new Todo.
*
* @param uuid the uuid
* @param title the title
* @param comment the comment
* @param dueDate the due date
* @param done Is the item done?
* @param prio Has the item high priority?
*/
protected Todo(String uuid, String title, String comment, Calendar dueDate, boolean done, boolean prio) {
this.uuid = uuid;
this.title = title;
this.comment = comment;
this.dueDate = dueDate;
this.done = done;
this.prio = prio;
}
/**
* Instantiates a new Todo.
*
* @param title the title
* @param comment the comment
*/
public Todo(String title, String comment) {
this.uuid = UUID.randomUUID().toString();
this.title = title;
this.comment = comment;
}
/**
* Gets uuid.
*
* @return the uuid
*/
public String getUuid() {
return this.uuid;
}
/**
* Gets title.
*
* @return the title
*/
public String getTitle() {
return title;
}
/**
* Sets title.
*
* @param title the title
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Is it done?
*
* @return the boolean
*/
public boolean isDone() {
return done;
}
/**
* Sets done.
*
* @param done the done
*/
public void setDone(boolean done) {
this.done = done;
}
/**
* Is prio.
*
* @return the boolean
*/
public boolean isPrio() {
return prio;
}
/**
* Sets prio.
*
* @param prio the prio
*/
public void setPrio(boolean prio) {
this.prio = prio;
}
/**
* Gets comment.
*
* @return the comment
*/
public String getComment() {
return comment;
}
/**
* Sets comment.
*
* @param comment the comment
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* Gets due date.
*
* @return the due date
*/
public Calendar getDueDate() {
return dueDate;
}
/**
* Sets due date.
*
* @param dueDate the due date
*/
public void setDueDate(Calendar dueDate) {
this.dueDate = dueDate;
}
public String getTime() {
SimpleDateFormat format = new SimpleDateFormat();
format.applyPattern("yyyyMMdd'T'HH:mm:ssZ");
return this.getDueDate() != null ? format.format(this.getDueDate().getTime()) : "00:00";
}
@Override
public String toString() {
return this.getTitle();
}
}

@ -0,0 +1,129 @@
package de.t_battermann.dhbw.todolist;
import java.util.*;
/**
* This class contains a todo list with all its items.
*/
public class TodoList {
private String uuid = UUID.randomUUID().toString();
private List<Todo> todos = new LinkedList<>();
private String name;
private boolean changeable;
/**
* Instantiates a new Todo list.
*
* @param name the name
*/
public TodoList(String name) {
this.changeable = true;
this.name = name;
}
/**
* Instantiates a new Todo list.
*
* @param name the name
* @param changeable Can the name be changed?
*/
public TodoList(String name, boolean changeable) {
this.changeable = changeable;
this.name = name;
this.initList();
}
/**
* Instantiates a new Todo list.
*
* @param uuid the uuid
* @param name the name
* @param changeable Can the name be changed?
*/
protected TodoList(String uuid, String name, boolean changeable) {
this.uuid = uuid;
this.todos = new LinkedList<>();
this.name = name;
this.changeable = changeable;
}
private void initList() {
this.todos.add(new Todo("Start using your TodoList", "Add, delete and modify entries."));
}
/**
* Gets uuid.
*
* @return the uuid
*/
public String getUuid() {
return this.uuid;
}
/**
* Gets name.
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Sets name.
*
* @param name the name
*/
public void setName(String name) {
if (this.isChangeable())
this.name = name;
}
/**
* Gets todos.
*
* @return the todos
*/
public List<Todo> getTodos() {
return todos;
}
/**
* Add todo.
*
* @param todo the todo
* @return the boolean
*/
public boolean addTodo(Todo todo) {
if (todos.contains(todo)) {
return false;
}
todos.add(todo);
return true;
}
/**
* Delete a todo item
*
* @param todo The Item to be deleted
*/
public void deleteTodo(Todo todo) {
if (todos.contains(todo)) {
todos.remove(todo);
}
}
/**
* Is changeable.
*
* @return true if the name can be changed
*/
public boolean isChangeable() {
return changeable;
}
@Override
public String toString() {
return this.getName();
}
}

@ -0,0 +1,205 @@
package de.t_battermann.dhbw.todolist;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.validator.routines.EmailValidator;
import java.io.Serializable;
import java.util.*;
/**
* This class contains all the users data.
*/
public class User implements Serializable {
private String username;
private String email;
private String password;
private String uuid = UUID.randomUUID().toString();
private List<TodoList> todoLists;
/**
* Instantiates a new User.
*
* @param username the username
* @param password the password
*/
public User(String username, String password) {
this.username = username;
this.password = hashPassword(password);
this.email = "";
todoLists = new LinkedList<>();
TodoList tmp = new TodoList("Default", false);
todoLists.add(tmp);
}
/**
* Instantiates a new User.
* Used to restore saved data
*
* @param uuid the uuid
* @param username the username
* @param hashedPassword the hashed password
* @param email the email
*/
protected User(String uuid, String username, String hashedPassword, String email) {
this.username = username;
this.uuid = uuid;
this.password = hashedPassword;
this.email = email;
this.todoLists = new LinkedList<>();
TodoList tmp = new TodoList("Default", false);
todoLists.add(tmp);
}
/**
* Gets username.
*
* @return the username
*/
public String getUsername() {
return this.username;
}
/**
* Gets password.
*
* @return the password
*/
protected String getPassword() {
return this.password;
}
/**
* Gets uuid.
*
* @return the uuid
*/
public String getUuid() {
return this.uuid;
}
/**
* Gets email.
*
* @return the email
*/
public String getEmail() {
return this.email;
}
/**
* Gets todo list.
*
* @param name the name
* @return the todo list
*/
public TodoList getTodoList(String name) {
for(TodoList l: todoLists)
if(l.getName().equals(name))
return l;
System.out.println("TodoList not found: " + name);
return null;
}
/**
* Gets todo lists.
*
* @return the todo lists
*/
public List<TodoList> getTodoLists() {
return todoLists;
}
/**
* Check login data.
*
* @param password the password
* @return the boolean
*/
public boolean checkLoginData(String password) {
System.out.println("pw: " + password + " -> " + this.hashPassword(password));
System.out.println("stored: " + this.password);
return this.hashPassword(password).equals(this.password);
}
@Override
public String toString() {
return "Username: " + username + "\n"
+ "eMail: " + email + "\n";
}
/**
* Add a todo list.
*
* @param name the name for the new list
* @return false if a list with the given name already exists
*/
public boolean addTodoList(String name) {
return this.addTodoList(new TodoList(name));
}
/**
* Add todo list.
*
* @param todoList the todo list
* @return false if a list with the given name already exists
*/
public boolean addTodoList(TodoList todoList) {
if ( this.getTodoList( todoList.getName()) == null ) {
this.todoLists.add(todoList);
return true;
}
System.out.println("A TodoList named '" + todoList.getName() + "' already exists!");
return false;
}
/**
* Update the users password
*
* @param password the password (cleartext)
*/
public void setPassword(String password) {
this.password = hashPassword(password);
}
/**
* Sets email.
*
* @param email the email
* @return the email
*/
public boolean setEmail(String email) {
if (User.checkEmail(email)) {
this.email = email;
return true;
}
System.out.println("Invalid eMail: '" + email + "'");
return false;
}
/**
* Generate a salted hash
*
* @param password the password to salt and hash
* @return salted and hashed password
*/
private String hashPassword(String password) {
return DigestUtils.sha256Hex(uuid + password);
}
/**
* Checks if eMail has correct syntax
* @param email string containing a eMail address
* @return true if valid syntax
*/
public static boolean checkEmail(String email) {
return email.length() != 0 && EmailValidator.getInstance().isValid(email);
}
public static boolean checkUsername(String username) {
return username.matches("[a-zA-Z0-9_-]{3,}");
}
public static boolean checkPassword(String password) {
return password.length() > 6 && password.matches(".*[A-Z].*") && password.matches(".*[a-z].*") && password.matches(".*[0-9].*");
}
}

@ -0,0 +1,286 @@
package de.t_battermann.dhbw.todolist;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* This class implement the ExportHandler interface. It converts the data to XML and vice versa.
*/
public class XMLHandler implements ExportHandler {
/**
* Convert a map containing the users to a DOMSource containing XML
*
* @param users The users map
* @return DOMSource containing XML
*/
private DOMSource doExport(Map<String, User> users) {
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement("todolistapp");
doc.appendChild(rootElement);
for (User userEntry : users.values()) {
Element user = doc.createElement("user");
Element username = doc.createElement("username");
username.appendChild(doc.createTextNode(userEntry.getUsername()));
user.appendChild(username);
Element password = doc.createElement("password");
password.appendChild(doc.createTextNode(userEntry.getPassword()));
user.appendChild(password);
Element password_salt = doc.createElement("uuid");
password_salt.appendChild(doc.createTextNode(userEntry.getUuid()));
user.appendChild(password_salt);
Element email = doc.createElement("email");
email.appendChild(doc.createTextNode(userEntry.getEmail()));
user.appendChild(email);
for (TodoList todoListEntry : userEntry.getTodoLists()) {
Element list = doc.createElement("TodoList");
list.setAttribute("changeable", todoListEntry.isChangeable() ? "true" : "false");
Element title = doc.createElement("name");
title.appendChild(doc.createTextNode(todoListEntry.getName()));
list.appendChild(title);
Element uuid = doc.createElement("uuid");
uuid.appendChild(doc.createTextNode(todoListEntry.getUuid()));
list.appendChild(uuid);
for (Todo entry : todoListEntry.getTodos()) {
SimpleDateFormat format = new SimpleDateFormat();
format.applyPattern("yyyyMMdd'T'HH:mm:ssZ");
Element todo = doc.createElement("item");
todo.setAttribute("prio", entry.isPrio() ? "true" : "false");
todo.setAttribute("done", entry.isDone() ? "true" : "false");
Element todoTitle = doc.createElement("title");
todoTitle.appendChild(doc.createTextNode(entry.getTitle()));
todo.appendChild(todoTitle);
Element todoUuid = doc.createElement("uuid");
todoUuid.appendChild(doc.createTextNode(entry.getUuid()));
todo.appendChild(todoUuid);
Element comment = doc.createElement("comment");
comment.appendChild(doc.createTextNode(entry.getComment()));
todo.appendChild(comment);
if (entry.getDueDate() != null) {
Element duedate = doc.createElement("duedate");
duedate.appendChild(doc.createTextNode(format.format(entry.getDueDate().getTime())));
todo.appendChild(duedate);
} // if duedate
list.appendChild(todo);
} // for todos
user.appendChild(list);
} // for todoLists
rootElement.appendChild(user);
} // for users
return new DOMSource(doc);
} catch (ParserConfigurationException e) {
e.printStackTrace();
return null;
}
}
public void exportToFile(Map<String, User> users, File file) {
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = this.doExport(users);
StreamResult result = new StreamResult(file);
transformer.transform(source, result);
} catch (TransformerException e) {
e.printStackTrace();
}
}
public String exportToString(Map<String, User> users) {
try {
StringWriter sw = new StringWriter();
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = this.doExport(users);
transformer.transform(source, new StreamResult(sw));
return sw.toString();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
return "";
} catch (TransformerException e) {
e.printStackTrace();
return "";
}
}
/**
* Get a string-element from a node
*
* @param node The node to be searched in
* @param name The elements name
* @return the desired string or empty string if not found
*/
private String elementGetString(Element node, String name) {
NodeList nodes = node.getElementsByTagName(name);
if (nodes.getLength() >= 1) {
return nodes.item(0).getTextContent();
}
return "";
}
/**
* Get a boolean value from a attribute
*
* @param node The node to be searched in
* @param name The attributes name
* @param defaultValue if the attribute is not set use this value
* @return either the value of the attribute or the default value if attribute not found
*/
private boolean elementGetBool(Element node, String name, boolean defaultValue) {
if (node.hasAttribute(name)) {
return node.getAttribute(name).compareTo("true") == 0;
}
return defaultValue;
}
/**
* Get a date from a element
*
* @param node The node to be searched in
* @param name The elements name
* @return Either the date (if found) or null
*/
private Calendar elementGetDate(Element node, String name) {
SimpleDateFormat format = new SimpleDateFormat();
format.applyPattern("yyyyMMdd'T'HH:mm:ssZ");
NodeList nodes = node.getElementsByTagName(name);
if (nodes.getLength() >= 1) {
try {
Calendar r = new GregorianCalendar();
r.setTime(format.parse(nodes.item(0).getTextContent()));
return r;
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Helper function to convert the xml to a map containing the user data
*
* @param stream InputStream containing XML data
* @return The user object
* @throws IOException
* @throws InvalidDataException
*/
private Map<String, User> doImport(InputStream stream) throws IOException, InvalidDataException {
// Temporary variables
TreeMap<String, User> users = new TreeMap<>();
User user;
TodoList todoList;
Todo todo;
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(stream);
doc.getDocumentElement().normalize();
// the actual import ...
if (!"todolistapp".equals(doc.getDocumentElement().getNodeName())) {
throw new InvalidDataException("Expected 'todolistapp' as root node");
}
NodeList nUsers = doc.getElementsByTagName("user");
for (int i = 0; i < nUsers.getLength(); i++) {
Node nUser = nUsers.item(i);
if (nUser.getNodeType() == Node.ELEMENT_NODE) {
Element eUser = (Element) nUser;
user = new User(
this.elementGetString(eUser, "uuid"),
this.elementGetString(eUser, "username"),
this.elementGetString(eUser, "password"),
this.elementGetString(eUser, "email")
);
NodeList nTodoLists = eUser.getElementsByTagName("TodoList");
for (int j = 0; j < nTodoLists.getLength(); j++) {
Node nTodoList = nTodoLists.item(j);
if (nTodoList.getNodeType() == Node.ELEMENT_NODE) {
Element eTodoList = (Element) nTodoList;
todoList = new TodoList(
this.elementGetString(eTodoList, "uuid"),
this.elementGetString(eTodoList, "name"),
this.elementGetBool(eTodoList, "changeable", true)
);
NodeList nTodos = eTodoList.getElementsByTagName("item");
for (int k = 0; k < nTodos.getLength(); k++) {
Node nTodo = nTodos.item(k);
if (nTodo.getNodeType() == Node.ELEMENT_NODE) {
Element eTodo = (Element) nTodo;
// String uuid, String title, String comment, Calendar dueDate, boolean done, boolean prio
todo = new Todo(
this.elementGetString(eTodo, "uuid"),
this.elementGetString(eTodo, "title"),
this.elementGetString(eTodo, "comment"),
this.elementGetDate(eTodo, "duedate"),
this.elementGetBool(eTodo, "done", false),
this.elementGetBool(eTodo, "prio", false)
);
todoList.addTodo(todo);
}
}
user.addTodoList(todoList);
}
}
users.put(user.getUsername(), user);
}
}
} catch (ParserConfigurationException | SAXException e) {
e.printStackTrace();
}
return users;
}
public Map<String, User> importFromFile(File file) throws IOException, InvalidDataException {
InputStream inputStream = new FileInputStream(file);
return this.doImport(inputStream);
}
public Map<String, User> importFromString(String str) throws InvalidDataException {
try {
InputStream inputStream = new ByteArrayInputStream(str.getBytes(Charset.forName("UTF-8")));
return this.doImport(inputStream);
} catch (IOException e) {
e.printStackTrace();
return new TreeMap<>();
}
}
}

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="350.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8.0.45" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Accordion>
<panes>
<TitledPane id="loginPane" animated="false" text="Login">
<content>
<VBox prefHeight="300.0" prefWidth="100.0">
<children>
<GridPane hgap="5.0" vgap="5.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="283.0" minWidth="10.0" prefWidth="159.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="419.0" minWidth="10.0" prefWidth="419.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Username:" />
<Label text="Password:" GridPane.rowIndex="1" />
<TextField id="loginUsername" GridPane.columnIndex="1" />
<PasswordField id="loginPassword" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
</GridPane>
<Button id="loginButton" mnemonicParsing="false" prefHeight="25.0" prefWidth="482.0" text="Login" textAlignment="CENTER" VBox.vgrow="ALWAYS">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</Button>
<Label id="labelHints" prefHeight="15.0" prefWidth="477.0" text="Please enter your credentials">
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
</Label>
</children>
</VBox>
</content>
</TitledPane>
<TitledPane id="createNewUserPane" animated="false" text="Create new user">
<content>
<VBox prefHeight="300.0" prefWidth="100.0">
<children>
<GridPane hgap="5.0" prefHeight="156.0" prefWidth="478.0" vgap="5.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="283.0" minWidth="10.0" prefWidth="159.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="419.0" minWidth="10.0" prefWidth="419.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Username:" />
<Label text="Password:" GridPane.rowIndex="1" />
<TextField id="registerUsername" GridPane.columnIndex="1" />
<PasswordField id="registerPassword" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="Password:" GridPane.rowIndex="2" />
<Label text="eMail:" GridPane.rowIndex="3" />
<Label text="eMail:" GridPane.rowIndex="4" />
<PasswordField id="registerPasswordRepeat" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<TextField id="registerEmail" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<TextField id="registerEmailRepeat" GridPane.columnIndex="1" GridPane.rowIndex="4" />
</children>
</GridPane>
<Button id="registerButton" mnemonicParsing="false" prefHeight="25.0" prefWidth="482.0" text="Login" textAlignment="CENTER" VBox.vgrow="ALWAYS">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</Button>
<Label id="labelHintsCreateNewUser" prefHeight="15.0" prefWidth="477.0" text="Create a new user">
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
</Label>
</children>
</VBox>
</content>
</TitledPane>
</panes>
</Accordion>
</children>
</VBox>

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<VBox id="vbox" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="550.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/8.0.45" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ToolBar>
<items>
<Button id="menuSave" mnemonicParsing="false" text="Save" />
<Button id="menuSaveAs" mnemonicParsing="false" text="Save as ..." />
<Separator orientation="VERTICAL" />
<Button id="menuChangePassword" mnemonicParsing="false" text="Cange password" />
<Button id="MenuChangeEmail" mnemonicParsing="false" text="Change eMail" />
<Button id="menuLogout" mnemonicParsing="false" text="Log out" />
<Separator orientation="VERTICAL" />
<Button id="menuClose" mnemonicParsing="false" text="Close" />
</items>
</ToolBar>
<SplitPane dividerPositions="0.333" prefHeight="160.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
<items>
<VBox>
<children>
<ListView id="todoLists" VBox.vgrow="ALWAYS" />
<ToolBar visible="false">
<items>
<TextField id="todoListNewName" />
<Button id="todoListNewNameSave" mnemonicParsing="false" text="Save" />
</items>
</ToolBar>
<ToolBar>
<items>
<Button mnemonicParsing="false" text="New" />
<Button mnemonicParsing="false" text="Delete" />
<Button mnemonicParsing="false" text="Edit" />
</items>
</ToolBar>
</children>
</VBox>
<VBox prefHeight="200.0" prefWidth="100.0">
<children>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button id="todoNew" mnemonicParsing="false" text="New" />
<Button id="todoToggleDone" mnemonicParsing="false" text="(un)done" />
<Button id="todoToggleStar" mnemonicParsing="false" text="(un)star" />
<Button id="todoDelete" mnemonicParsing="false" text="delete" />
<Separator orientation="VERTICAL" />
<CheckBox id="todosShowDone" mnemonicParsing="false" text="show done" />
</items>
</ToolBar>
<SplitPane dividerPositions="0.6" orientation="VERTICAL" VBox.vgrow="ALWAYS">
<items>
<ListView id="todos" prefHeight="200.0" prefWidth="200.0" />
<ScrollPane vbarPolicy="ALWAYS">
<content>
<VBox>
<children>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS" maxWidth="200.0" minWidth="10.0" prefWidth="105.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="309.0" minWidth="10.0" prefWidth="309.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" prefHeight="75.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Description" GridPane.rowIndex="1" />
<Label text="Due date" GridPane.rowIndex="2" />
<Label text="Title" />
<TextField id="todoDetailTitle" GridPane.columnIndex="1" />
<TextArea id="todoDetailDescription" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2">
<children>
<CheckBox id="todoDetailDueDate" mnemonicParsing="false" />
<DatePicker id="todoDetailDate" disable="true" />
<TextField id="todoDetailTime" disable="true" text="12:00" />
</children>
</HBox>
</children>
</GridPane>
<Button id="todoDetailSave" mnemonicParsing="false" style="-fx-pref-width: 100%;" text="Save" VBox.vgrow="ALWAYS" />
</children>
</VBox>
</content>
</ScrollPane>
</items>
</SplitPane>
</children>
</VBox>
</items>
</SplitPane>
<Label id="statusLine" text="TodoList" />
</children>
</VBox>

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8.0.45" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TitledPane animated="false" collapsible="false" prefHeight="75.0" text="Load saved data">
<content>
<HBox prefWidth="200.0">
<children>
<TextField id="openFilePath" prefHeight="26.0" prefWidth="346.0">
<HBox.margin>
<Insets right="5.0" />
</HBox.margin>
</TextField>
<Button id="openFileButton" mnemonicParsing="false" prefHeight="26.0" prefWidth="127.0" text="Open file" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" prefHeight="75.0" text="New file">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="0.0" prefWidth="498.0">
<children>
<Button id="openFileNew" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="473.0" text="Create new file" AnchorPane.bottomAnchor="1.0" AnchorPane.leftAnchor="1.0" AnchorPane.rightAnchor="1.0" AnchorPane.topAnchor="1.0" />
</children></AnchorPane>
</content>
</TitledPane>
</children>
</VBox>

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<TitledPane animated="false" collapsible="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" text="Save as ..." xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.45">
<content>
<HBox>
<children>
<TextField id="filename">
<HBox.margin>
<Insets right="5.0" />
</HBox.margin>
</TextField>
<Button id="save" mnemonicParsing="false" text="Save" />
</children>
</HBox>
</content>
</TitledPane>
Loading…
Cancel
Save