package mi.hdm.filesystem; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import mi.hdm.TastyPages; import mi.hdm.mealPlan.MealPlan; import mi.hdm.recipes.*; import mi.hdm.shoppingList.ShoppingList; import mi.hdm.typeAdapters.CategoryManagerTypeAdapter; import mi.hdm.typeAdapters.MealPlanTypeAdapter; import mi.hdm.typeAdapters.RecipeTypeAdapter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; import java.math.BigDecimal; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Stream; /** * Containing methods to serialize and deserialize parts of the app content for saving to / loading from the filesystem */ public class FileManager { private final static String PATH_TO_DEFAULT_INGREDIENTS = "/data/nutrition.csv"; private final static String PATH_TO_USER_DATA = System.getProperty("user.home") + "\\AppData\\Roaming\\TastyPages"; private final static Logger log = LogManager.getLogger(FileManager.class); public static void serializeToFile(TastyPages app) { } public static TastyPages deserializeFromFile() throws Exception { //TODO: if this fails, log.fatal and System.exit log.info("Trying to read {}", PATH_TO_USER_DATA); Path userPath = Path.of(PATH_TO_USER_DATA); if (userPath.toFile().mkdirs()) { //If .mkdirs() returns true, that means the folder has not existed before -> in this case, only load default ingredients! log.info("TastyPages folder has been created at {}", userPath); IngredientManager deserializedIngredientManager = deserializeIngredientManager(getAbsolutePathFromResourceFolder()); return new TastyPages(deserializedIngredientManager); } else { //otherwise, read user data log.info("Found TastyPages folder at {}, loading user data from there.", userPath); IngredientManager ingredientManager; RecipeManager recipeManager; CategoryManager categoryManager; MealPlan mealPlan; ShoppingList shoppingList; if (Files.exists(Path.of(userPath + "ingredients.csv"))) { ingredientManager = deserializeIngredientManager(PATH_TO_USER_DATA); } else { ingredientManager = deserializeIngredientManager(getAbsolutePathFromResourceFolder()); } Path recipeFolder = Path.of(userPath + "/recipes"); if (Files.exists(recipeFolder) && Files.isDirectory(recipeFolder)) { recipeManager = deserializeRecipeManager(PATH_TO_USER_DATA + "/recipes", ingredientManager); } else { recipeFolder.toFile().mkdir(); recipeManager = new RecipeManager(); } Path toCategoryJson = Path.of(userPath + "/categories.json"); if (Files.exists(toCategoryJson) && Files.isRegularFile(toCategoryJson)) { categoryManager = deserializeCategoryManager(toCategoryJson); } else { categoryManager = new CategoryManager(); } Path toMealPlanJson = Path.of(userPath + "/mealplan.json"); if (Files.exists(toMealPlanJson) && Files.isRegularFile(toMealPlanJson)) { mealPlan = deserializeMealPlan(toMealPlanJson, recipeManager); } else { mealPlan = new MealPlan(recipeManager); } return null; } } private static IngredientManager deserializeIngredientManager(String path) throws IOException { CSVParser parser = new CSVParser(); List<Ingredient> ingredients = parser.getIngredientsFromCSV( path, ',', "name", "calories", "carbohydrate", "fat", "protein", "fiber", "sodium" ); return new IngredientManager(ingredients); } private static RecipeManager deserializeRecipeManager(String path, IngredientManager ingredientManager) throws IOException { Path recipePath = Path.of(path); List<Path> recipePaths; log.info("Reading recipes from '{}'", path); try (Stream<Path> stream = Files.list(recipePath)) { //TODO: catch clause recipePaths = stream.toList(); } List<Recipe> recipes = recipePaths .stream() .map(p -> { try { return Files.readString(p); } catch (IOException e) { e.printStackTrace(); log.error("Could not read file contents for {}. Check file permissions and content", p); return null; } }) .filter(Objects::nonNull) .map(json -> JSONtoRecipe(json, ingredientManager)) .toList(); return new RecipeManager(recipes); } private static CategoryManager deserializeCategoryManager(Path path) throws IOException { log.info("Reading categories from {}", path); Gson gson = new GsonBuilder().registerTypeAdapter(CategoryManager.class, new CategoryManagerTypeAdapter()).create(); String fileContent = Files.readString(path); return gson.fromJson(fileContent, CategoryManager.class); } private static MealPlan deserializeMealPlan(Path filepath, RecipeManager recipeManager) throws IOException { Gson gson = new GsonBuilder() .registerTypeAdapter(MealPlan.class, new MealPlanTypeAdapter(recipeManager)) .create(); String fileContent = Files.readString(filepath); MealPlan mealPlan = gson.fromJson(fileContent, MealPlan.class); return null; } private static ShoppingList deserializeShoppingList() { return null; } private static String recipeToJSON(Recipe recipe, IngredientManager ingredientManager) { Gson gson = new GsonBuilder() .registerTypeAdapter(Recipe.class, new RecipeTypeAdapter(ingredientManager)) .create(); return gson.toJson(recipe); } /** * Used to convert an ingredient into a CSV String seperated by comma * * @return a comma seperated String containing the name of the ingredient as well as the contents of its nutrition table */ private static String ingredientToCSV(Ingredient ingredient) { //"name", "calories", "carbohydrate", "fat", "protein", "fiber", "sodium" Map<Nutrition, BigDecimal> map = ingredient.getNutritionTable().getTable(); return String.format( "%s, %f, %f, %f, %f, %f, %f%n", ingredient.getName(), map.get(Nutrition.CALORIES).doubleValue(), map.get(Nutrition.CARBS).doubleValue(), map.get(Nutrition.FAT).doubleValue(), map.get(Nutrition.PROTEINS).doubleValue(), map.get(Nutrition.FIBERS).doubleValue(), map.get(Nutrition.SALT).doubleValue() ); } private static Recipe JSONtoRecipe(String json, IngredientManager ingredientManager) { Gson gson = new GsonBuilder().registerTypeAdapter(Recipe.class, new RecipeTypeAdapter(ingredientManager)).create(); return gson.fromJson(json, Recipe.class); } private static String getAbsolutePathFromResourceFolder() throws URISyntaxException { URL resourceUrl = FileManager.class.getResource(FileManager.PATH_TO_DEFAULT_INGREDIENTS); assert resourceUrl != null; Path path = Paths.get(resourceUrl.toURI()); return path.toFile().getAbsolutePath(); } }