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.CategoryTypeAdapter; 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.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); private final static Gson gson = new Gson(); 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(PATH_TO_DEFAULT_INGREDIENTS)); 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_TO_DEFAULT_INGREDIENTS)); } 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(); } 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 Exception { Path recipePath = Path.of(path); List<Path> recipePaths; 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 ""; } }) .map(FileManager::JSONtoRecipe) .toList(); return new RecipeManager(recipes); } private static CategoryManager deserializeCategoryManager(Path path) { Gson gson = new GsonBuilder().registerTypeAdapter(Category.class, new CategoryTypeAdapter()).create(); return null; } private static MealPlan deserializeMealPlan() { 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) { //when loading a recipe, is it important that its ingredients are the same objects as in the ingredient manager? gson.fromJson(json, Recipe.class); return null; } private static String getAbsolutePathFromResourceFolder(String resource) throws URISyntaxException { URL resourceUrl = FileManager.class.getResource(resource); assert resourceUrl != null; Path path = Paths.get(resourceUrl.toURI()); return path.toFile().getAbsolutePath(); } }