diff --git a/src/de/deadlocker8/budgetmaster/logic/utils/FileHelper.java b/src/de/deadlocker8/budgetmaster/logic/utils/FileHelper.java index 3b0c4da6d85cf27f17078403097d5649d4110f26..5c165365ed1c2316e81d111b9a33ffb03b1b4e4a 100644 --- a/src/de/deadlocker8/budgetmaster/logic/utils/FileHelper.java +++ b/src/de/deadlocker8/budgetmaster/logic/utils/FileHelper.java @@ -1,5 +1,6 @@ package de.deadlocker8.budgetmaster.logic.utils; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.Reader; @@ -7,24 +8,29 @@ import java.io.Writer; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.ArrayList; import com.google.gson.Gson; import de.deadlocker8.budgetmaster.logic.Settings; +import de.deadlocker8.budgetmaster.logic.tag.Tag; +import de.deadlocker8.budgetmaster.logic.tag.TagMatch; import de.deadlocker8.budgetmasterserver.logic.database.Database; +import de.deadlocker8.budgetmasterserver.logic.database.OldDatabase; import tools.Localization; import tools.PathUtils; +@SuppressWarnings("deprecation") public class FileHelper -{ +{ public static Settings loadSettings() { Settings settings; try { - Gson gson = new Gson(); + Gson gson = new Gson(); Reader reader = Files.newBufferedReader(Paths.get(PathUtils.getOSindependentPath() + Localization.getString(Strings.FOLDER) + "/settings.json"), Charset.forName("UTF-8")); - settings = gson.fromJson(reader, Settings.class); + settings = gson.fromJson(reader, Settings.class); reader.close(); return settings; } @@ -33,9 +39,9 @@ public class FileHelper return null; } } - + public static void saveSettings(Settings settings) throws IOException - { + { Gson gson = new Gson(); String jsonString = gson.toJson(settings); PathUtils.checkFolder(new File(PathUtils.getOSindependentPath() + Localization.getString(Strings.FOLDER))); @@ -43,28 +49,66 @@ public class FileHelper writer.write(jsonString); writer.close(); } - + public static Database loadDatabaseJSON(File file) throws IOException - { + { Gson gson = new Gson(); - Reader reader = Files.newBufferedReader(Paths.get(file.getAbsolutePath()), Charset.forName("UTF-8")); - Database database = gson.fromJson(reader, Database.class); + BufferedReader reader = Files.newBufferedReader(Paths.get(file.getAbsolutePath()), Charset.forName("UTF-8")); + + StringBuilder sb = new StringBuilder(); + String line; + while((line = reader.readLine()) != null) + { + sb.append(line); + } + reader.close(); - return database; + String jsonString = sb.toString(); + if(jsonString.contains("BUDGETMASTER_DATABASE")) + { + if(jsonString.contains("VERSION")) + { + int start = jsonString.indexOf("\"VERSION\": "); + start = start + 11; + int version = Integer.parseInt(jsonString.substring(start, start + 1)); + Database database; + + switch(version) + { + case 2: database = gson.fromJson(jsonString, Database.class); + break; + default: return loadOldDatabase(gson, jsonString); + } + return database; + } + } + + return loadOldDatabase(gson, jsonString); } + private static Database loadOldDatabase(Gson gson, String jsonString) throws IOException + { + // database version = 1 (prior to BudgetMaster 1.6.0) + OldDatabase olDatabase = gson.fromJson(jsonString, OldDatabase.class); + return new Database(olDatabase.getCategories(), + olDatabase.getNormalPayments(), + olDatabase.getRepeatingPayments(), + new ArrayList<Tag>(), + new ArrayList<TagMatch>()); + } + public static void saveDatabaseJSON(File file, String databaseJSON) throws IOException - { + { Writer writer = Files.newBufferedWriter(Paths.get(file.getAbsolutePath()), Charset.forName("UTF-8")); writer.write(databaseJSON); writer.close(); } - + public static Object loadObjectFromJSON(String fileName, Object objectype) - { + { try { - Gson gson = new Gson(); + Gson gson = new Gson(); Reader reader = Files.newBufferedReader(Paths.get(PathUtils.getOSindependentPath() + Localization.getString(Strings.FOLDER) + "/" + fileName + ".json"), Charset.forName("UTF-8")); Object preferences = gson.fromJson(reader, objectype.getClass()); reader.close(); @@ -75,9 +119,9 @@ public class FileHelper return null; } } - + public static void saveObjectToJSON(String fileName, Object objectToSave) throws IOException - { + { Gson gson = new Gson(); String jsonString = gson.toJson(objectToSave); PathUtils.checkFolder(new File(PathUtils.getOSindependentPath() + Localization.getString(Strings.FOLDER))); diff --git a/src/de/deadlocker8/budgetmaster/ui/controller/SettingsController.java b/src/de/deadlocker8/budgetmaster/ui/controller/SettingsController.java index 43a079c1288a0e7062b79a04b591907cba43b93d..5a87fc9550622192f54d19770b55a22a593524e1 100644 --- a/src/de/deadlocker8/budgetmaster/ui/controller/SettingsController.java +++ b/src/de/deadlocker8/budgetmaster/ui/controller/SettingsController.java @@ -414,7 +414,11 @@ public class SettingsController implements Styleable try { database = FileHelper.loadDatabaseJSON(file); - if(database.getCategories() == null || database.getNormalPayments() == null || database.getRepeatingPayments() == null) + if(database.getCategories() == null + || database.getNormalPayments() == null + || database.getRepeatingPayments() == null + || database.getTags() == null + || database.getTagMatches() == null) { AlertGenerator.showAlert(AlertType.ERROR, Localization.getString(Strings.TITLE_ERROR), diff --git a/src/de/deadlocker8/budgetmasterserver/logic/database/Database.java b/src/de/deadlocker8/budgetmasterserver/logic/database/Database.java index 849d5991bf0927eb9c89b36d3c681e62964c0abf..e398a5e7ee1d6111b35e3c603f9ea5755f50a3cb 100644 --- a/src/de/deadlocker8/budgetmasterserver/logic/database/Database.java +++ b/src/de/deadlocker8/budgetmasterserver/logic/database/Database.java @@ -20,8 +20,10 @@ public class Database * added tags and tag matches (additional tables) */ + @SuppressWarnings("unused") + private final String TYPE = "BUDGETMASTER_DATABASE"; private final int VERSION = 2; - private ArrayList<Category> categories; + private ArrayList<Category> categoriesX; private ArrayList<NormalPayment> normalPayments; private ArrayList<RepeatingPayment> repeatingPayments; private ArrayList<Tag> tags; @@ -32,18 +34,9 @@ public class Database } - public Database(ArrayList<Category> categories, ArrayList<NormalPayment> normalPayments, ArrayList<RepeatingPayment> repeatingPayments) - { - this.categories = categories; - this.normalPayments = normalPayments; - this.repeatingPayments = repeatingPayments; - this.tags = new ArrayList<>(); - this.tagMatches = new ArrayList<>(); - } - public Database(ArrayList<Category> categories, ArrayList<NormalPayment> normalPayments, ArrayList<RepeatingPayment> repeatingPayments, ArrayList<Tag> tags, ArrayList<TagMatch> tagMatches) { - this.categories = categories; + this.categoriesX = categories; this.normalPayments = normalPayments; this.repeatingPayments = repeatingPayments; this.tags = tags; @@ -52,7 +45,7 @@ public class Database public ArrayList<Category> getCategories() { - return categories; + return categoriesX; } public ArrayList<NormalPayment> getNormalPayments() @@ -83,6 +76,6 @@ public class Database @Override public String toString() { - return "Database [VERSION=" + VERSION + ", categories=" + categories + ", normalPayments=" + normalPayments + ", repeatingPayments=" + repeatingPayments + ", tags=" + tags + ", tagMatches=" + tagMatches + "]"; + return "Database [VERSION=" + VERSION + ", categories=" + categoriesX + ", normalPayments=" + normalPayments + ", repeatingPayments=" + repeatingPayments + ", tags=" + tags + ", tagMatches=" + tagMatches + "]"; } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseHandler.java b/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseHandler.java index f2ddf9ca7bef7706de22d6993522a6cc5bc69911..05d82c97bcd7328670ebcb61c46d184cca79966c 100644 --- a/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseHandler.java +++ b/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseHandler.java @@ -724,6 +724,8 @@ public class DatabaseHandler String tablePayment = "DROP TABLE IF EXISTS payment;"; String tableRepeatingPayment = "DROP TABLE IF EXISTS repeating_payment;"; String tableRepeatingEntry = "DROP TABLE IF EXISTS repeating_entry;"; + String tableTag = "DROP TABLE IF EXISTS tag;"; + String tableTagMatch = "DROP TABLE IF EXISTS tag_match;"; try { stmt = connection.createStatement(); @@ -736,6 +738,10 @@ public class DatabaseHandler Logger.info("Deleted table: repeating_payment"); stmt.execute(tableRepeatingEntry); Logger.info("Deleted table: repeating_entry"); + stmt.execute(tableTag); + Logger.info("Deleted table: tag"); + stmt.execute(tableTagMatch); + Logger.info("Deleted table: tag_match"); stmt.execute("SET FOREIGN_KEY_CHECKS = 1;"); } catch(SQLException e) diff --git a/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseImporter.java b/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseImporter.java index 98c4424418742b4c0c0789c89fecf3e19aac22fd..3f5d1a54dca8f7feafcde5ed188c803fb8ff64a7 100644 --- a/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseImporter.java +++ b/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseImporter.java @@ -6,19 +6,26 @@ import java.util.Iterator; import de.deadlocker8.budgetmaster.logic.category.Category; import de.deadlocker8.budgetmaster.logic.payment.NormalPayment; import de.deadlocker8.budgetmaster.logic.payment.RepeatingPayment; +import de.deadlocker8.budgetmaster.logic.tag.Tag; +import de.deadlocker8.budgetmaster.logic.tag.TagMatch; public class DatabaseImporter { private DatabaseHandler handler; + private DatabaseTagHandler tagHandler; private ArrayList<Category> categories; private ArrayList<NormalPayment> normalPayments; private ArrayList<RepeatingPayment> repeatingPayments; private ArrayList<NormalPayment> changedNormalPayments; private ArrayList<RepeatingPayment> changedRepeatingPayments; + private ArrayList<Tag> tags; + private ArrayList<TagMatch> tagMatches; + private ArrayList<TagMatch> changedTagMatches; - public DatabaseImporter(DatabaseHandler handler) throws IllegalStateException + public DatabaseImporter(DatabaseHandler handler, DatabaseTagHandler tagHandler) throws IllegalStateException { this.handler = handler; + this.tagHandler = tagHandler; } public void importDatabase(Database database) @@ -28,6 +35,9 @@ public class DatabaseImporter this.repeatingPayments = database.getRepeatingPayments(); this.changedNormalPayments = new ArrayList<>(); this.changedRepeatingPayments = new ArrayList<>(); + this.tags = database.getTags(); + this.tagMatches = database.getTagMatches(); + this.changedTagMatches = new ArrayList<>(); importAll(); } @@ -55,7 +65,34 @@ public class DatabaseImporter repeatingPayments.addAll(changedRepeatingPayments); importNormalPayments(normalPayments); + tagMatches.addAll(changedTagMatches); + changedTagMatches = new ArrayList<>(); + importRepeatingPayments(repeatingPayments); + tagMatches.addAll(changedTagMatches); + changedTagMatches = new ArrayList<>(); + + // import tags + for(Tag currentTag : tags) + { + int tagID = currentTag.getID(); + + Tag existingTag = tagHandler.getTagByName(currentTag.getName()); + if(existingTag == null) + { + tagHandler.addTag(currentTag.getName()); + int newID = tagHandler.getLastInsertID(); + + updateTagMatchesByTagID(tagID, newID); + } + else + { + updateTagMatchesByTagID(tagID, existingTag.getID()); + } + } + + tagMatches.addAll(changedTagMatches); + importTagMatches(tagMatches); } private void updatePayments(int oldID, int newID) @@ -68,7 +105,15 @@ public class DatabaseImporter if(currentPayment.getCategoryID() == oldID) { currentPayment.setCategoryID(newID); - //remove payment from list to avoid overriding category ID again in the future + /* + * remove payment from payments list to avoid overriding category ID again on future calls of this method + * e.g.: call 1 = replace ID 2 with 3 + * call 2 = replace ID 3 with 4 + * --> would replace category IDs in payments where category ID has already been replaced + * --> would lead to wrong import + * --> remove payment from list but add to "changedPayments" in order not to loose the payment completly + * --> remaining payments in list and all payments from "changedPayments" will be merged after all categories are imported + */ changedNormalPayments.add(currentPayment); iterator.remove(); } @@ -82,7 +127,9 @@ public class DatabaseImporter if(currentPayment.getCategoryID() == oldID) { currentPayment.setCategoryID(newID); - //remove payment from list to avoid overriding category ID again in the future + /* + * see explanation in NormalPayments loop + */ changedRepeatingPayments.add(currentPayment); iterator2.remove(); } @@ -93,7 +140,8 @@ public class DatabaseImporter { for(NormalPayment currentPayment : normalPayments) { - handler.addNormalPayment(currentPayment.getAmount(), currentPayment.getDate(), currentPayment.getCategoryID(), currentPayment.getName(), currentPayment.getDescription()); + int newID = handler.addNormalPayment(currentPayment.getAmount(), currentPayment.getDate(), currentPayment.getCategoryID(), currentPayment.getName(), currentPayment.getDescription()); + updateTagMatchesByPaymentID(currentPayment.getID(), newID); } } @@ -101,7 +149,80 @@ public class DatabaseImporter { for(RepeatingPayment currentPayment : repeatingPayments) { - handler.addRepeatingPayment(currentPayment.getAmount(), currentPayment.getDate(), currentPayment.getCategoryID(), currentPayment.getName(), currentPayment.getDescription(), currentPayment.getRepeatInterval(), currentPayment.getRepeatEndDate(), currentPayment.getRepeatMonthDay()); + int newID = handler.addRepeatingPayment(currentPayment.getAmount(), currentPayment.getDate(), currentPayment.getCategoryID(), currentPayment.getName(), currentPayment.getDescription(), currentPayment.getRepeatInterval(), currentPayment.getRepeatEndDate(), currentPayment.getRepeatMonthDay()); + updateTagMatchesByRepeatingPaymentID(currentPayment.getID(), newID); } - } + } + + private void updateTagMatchesByTagID(int oldID, int newID) + { + //check tag matches for old tag ID + Iterator<TagMatch> iterator = tagMatches.iterator(); + while(iterator.hasNext()) + { + TagMatch currentTagMatch = iterator.next(); + if(currentTagMatch.getTagID() == oldID) + { + currentTagMatch.setTagID(newID); + /* + * see explanation in updatePayments() + */ + changedTagMatches.add(currentTagMatch); + iterator.remove(); + } + } + } + + private void updateTagMatchesByPaymentID(int oldID, int newID) + { + //check tag matches for old payment ID + Iterator<TagMatch> iterator = tagMatches.iterator(); + while(iterator.hasNext()) + { + TagMatch currentTagMatch = iterator.next(); + if(currentTagMatch.getPaymentID() == oldID) + { + currentTagMatch.setPaymentID(newID); + /* + * see explanation in updatePayments() + */ + changedTagMatches.add(currentTagMatch); + iterator.remove(); + } + } + } + + private void updateTagMatchesByRepeatingPaymentID(int oldID, int newID) + { + //check tag matches for old payment ID + Iterator<TagMatch> iterator = tagMatches.iterator(); + while(iterator.hasNext()) + { + TagMatch currentTagMatch = iterator.next(); + if(currentTagMatch.getRepeatingPaymentID() == oldID) + { + currentTagMatch.setRepeatingPaymentID(newID); + /* + * see explanation in updatePayments() + */ + changedTagMatches.add(currentTagMatch); + iterator.remove(); + } + } + } + + private void importTagMatches(ArrayList<TagMatch> tagMatches) + { + for(TagMatch currentTagMatch : tagMatches) + { + if(currentTagMatch.getRepeatingPaymentID() == -1) + { + tagHandler.addTagMatchForPayment(currentTagMatch.getTagID(), currentTagMatch.getPaymentID()); + } + else + { + tagHandler.addTagMatchForRepeatingPayment(currentTagMatch.getTagID(), currentTagMatch.getRepeatingPaymentID()); + } + } + } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseTagHandler.java b/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseTagHandler.java index 9da524b4e953093228d1516fb1dcfc15d9673ab3..9067c035793e6f9eaf43a4bc60ca79d4d64bba2d 100644 --- a/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseTagHandler.java +++ b/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseTagHandler.java @@ -43,6 +43,32 @@ public class DatabaseTagHandler } } + public int getLastInsertID() + { + PreparedStatement stmt = null; + int lastInsertID = 0; + try + { + stmt = connection.prepareStatement("SELECT LAST_INSERT_ID();"); + ResultSet rs = stmt.executeQuery(); + + while(rs.next()) + { + lastInsertID = rs.getInt("LAST_INSERT_ID()"); + } + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); + } + + return lastInsertID; + } + public ArrayList<Tag> getAllTags() { PreparedStatement stmt = null; diff --git a/src/de/deadlocker8/budgetmasterserver/logic/database/OldDatabase.java b/src/de/deadlocker8/budgetmasterserver/logic/database/OldDatabase.java new file mode 100644 index 0000000000000000000000000000000000000000..c38caec0e345887aa1eda6ee78a692557246a94e --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/logic/database/OldDatabase.java @@ -0,0 +1,45 @@ +package de.deadlocker8.budgetmasterserver.logic.database; + +import java.util.ArrayList; + +import de.deadlocker8.budgetmaster.logic.category.Category; +import de.deadlocker8.budgetmaster.logic.payment.NormalPayment; +import de.deadlocker8.budgetmaster.logic.payment.RepeatingPayment; + +/** + * this class is only used for backwards compatibility while importing database jsons files + */ +@Deprecated +public class OldDatabase +{ + private ArrayList<Category> categories; + private ArrayList<NormalPayment> normalPayments; + private ArrayList<RepeatingPayment> repeatingPayments; + + public OldDatabase() + { + + } + + public OldDatabase(ArrayList<Category> categories, ArrayList<NormalPayment> normalPayments, ArrayList<RepeatingPayment> repeatingPayments) + { + this.categories = categories; + this.normalPayments = normalPayments; + this.repeatingPayments = repeatingPayments; + } + + public ArrayList<Category> getCategories() + { + return categories; + } + + public ArrayList<NormalPayment> getNormalPayments() + { + return normalPayments; + } + + public ArrayList<RepeatingPayment> getRepeatingPayments() + { + return repeatingPayments; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java index 86d6552d3328f59b96df0dca4fe69c8dfe6bcb34..3d2ac6bba7caae97964b8b2f0adf8546e10b17f0 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java +++ b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java @@ -161,7 +161,7 @@ public class SparkServer // Database get("/database", new DatabaseExport(settings, gson)); - post("/database", new DatabaseImport(handler, gson)); + post("/database", new DatabaseImport(handler, tagHandler, gson)); delete("/database", new DatabaseDelete(handler, settings)); get("/version", new VersionGet(gson, versionInfo)); diff --git a/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseImport.java b/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseImport.java index 86dce0c839fd30528cf4cb467c4f5907287a4668..b5cccfe8d8135103f18df8df89e4b8f901b5662c 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseImport.java +++ b/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseImport.java @@ -7,6 +7,7 @@ import com.google.gson.Gson; import de.deadlocker8.budgetmasterserver.logic.database.Database; import de.deadlocker8.budgetmasterserver.logic.database.DatabaseHandler; import de.deadlocker8.budgetmasterserver.logic.database.DatabaseImporter; +import de.deadlocker8.budgetmasterserver.logic.database.DatabaseTagHandler; import logger.Logger; import spark.Request; import spark.Response; @@ -15,11 +16,13 @@ import spark.Route; public class DatabaseImport implements Route { private DatabaseHandler handler; + private DatabaseTagHandler tagHandler; private Gson gson; - public DatabaseImport(DatabaseHandler handler, Gson gson) + public DatabaseImport(DatabaseHandler handler, DatabaseTagHandler tagHandler, Gson gson) { this.handler = handler; + this.tagHandler = tagHandler; this.gson = gson; } @@ -32,7 +35,7 @@ public class DatabaseImport implements Route { Database database = gson.fromJson(databaseJSON, Database.class); - DatabaseImporter importer = new DatabaseImporter(handler); + DatabaseImporter importer = new DatabaseImporter(handler, tagHandler); importer.importDatabase(database); return ""; } diff --git a/tests/de/deadlocker8/budgetmaster/tests/server/database/DatabaseImportExportTest.java b/tests/de/deadlocker8/budgetmaster/tests/server/database/DatabaseImportExportTest.java index 4c7975e93c0c42523e448b4c29ed0d1aafb46c78..ec87f5a9439f7d5ad7f4e0cdfdf0dca8d210f319 100644 --- a/tests/de/deadlocker8/budgetmaster/tests/server/database/DatabaseImportExportTest.java +++ b/tests/de/deadlocker8/budgetmaster/tests/server/database/DatabaseImportExportTest.java @@ -26,12 +26,14 @@ import de.deadlocker8.budgetmasterserver.logic.database.Database; import de.deadlocker8.budgetmasterserver.logic.database.DatabaseExporter; import de.deadlocker8.budgetmasterserver.logic.database.DatabaseHandler; import de.deadlocker8.budgetmasterserver.logic.database.DatabaseImporter; +import de.deadlocker8.budgetmasterserver.logic.database.DatabaseTagHandler; import tools.Localization; public class DatabaseImportExportTest { private static Settings settings; private static DatabaseHandler databaseHandler; + private static DatabaseTagHandler tagHandler; @BeforeClass public static void init() @@ -44,6 +46,7 @@ public class DatabaseImportExportTest handler.deleteDatabase(); handler = new DatabaseHandler(settings); databaseHandler = handler; + tagHandler = new DatabaseTagHandler(settings); Localization.init("de/deadlocker8/budgetmaster/resources/"); Localization.loadLanguage(Locale.GERMANY); @@ -62,7 +65,7 @@ public class DatabaseImportExportTest File file = Paths.get("tests/de/deadlocker8/budgetmaster/tests/resources/import.json").toFile(); Database database = FileHelper.loadDatabaseJSON(file); - DatabaseImporter importer = new DatabaseImporter(databaseHandler); + DatabaseImporter importer = new DatabaseImporter(databaseHandler, tagHandler); importer.importDatabase(database); //test category @@ -112,7 +115,7 @@ public class DatabaseImportExportTest File file = Paths.get("tests/de/deadlocker8/budgetmaster/tests/resources/import.json").toFile(); Database database = FileHelper.loadDatabaseJSON(file); - DatabaseImporter importer = new DatabaseImporter(databaseHandler); + DatabaseImporter importer = new DatabaseImporter(databaseHandler, tagHandler); importer.importDatabase(database); file = Paths.get("tests/de/deadlocker8/budgetmaster/tests/resources/export.json").toFile();