From 0356930efb54cb3c0b84f30c5677840156f619ce Mon Sep 17 00:00:00 2001 From: OxFF Date: Mon, 14 Apr 2025 03:13:07 +0300 Subject: [PATCH] We are the wild! --- .project | 2 +- .settings/org.eclipse.buildship.core.prefs | 13 +- app/.classpath | 7 + .../org.eclipse.buildship.core.prefs | 14 +- app/src/main/java/itmo/lab5/App.java | 34 ++- .../java/itmo/lab5/cli/CommandBuilder.java | 23 +- .../java/itmo/lab5/cli/CommandContext.java | 18 +- .../java/itmo/lab5/cli/CommandInvoker.java | 20 ++ .../java/itmo/lab5/cli/CommandRegistry.java | 25 +- .../itmo/lab5/cli/commands/ClearCommand.java | 38 ++- .../lab5/cli/commands/ExecuteCommand.java | 96 ++++---- .../itmo/lab5/cli/commands/ExitCommand.java | 26 ++- .../itmo/lab5/cli/commands/FieldCommand.java | 29 ++- .../itmo/lab5/cli/commands/FilterCommand.java | 51 ++-- .../itmo/lab5/cli/commands/HelpCommand.java | 51 ++-- .../lab5/cli/commands/HistoryCommand.java | 45 ++-- .../itmo/lab5/cli/commands/InfoCommand.java | 54 +++-- .../itmo/lab5/cli/commands/InsertCommand.java | 153 +++++++----- .../lab5/cli/commands/RemoveKeyCommand.java | 73 +++--- .../lab5/cli/commands/ReplaceCommand.java | 221 ++++++++++++++++-- .../itmo/lab5/cli/commands/SaveCommand.java | 92 +++----- .../itmo/lab5/cli/commands/ShowCommand.java | 51 ++-- .../itmo/lab5/cli/commands/UpdateCommand.java | 180 +++++++------- .../java/itmo/lab5/cli/helpers/History.java | 33 ++- .../itmo/lab5/cli/helpers/ReaderUtil.java | 57 ++++- .../java/itmo/lab5/interfaces/Command.java | 14 +- .../java/itmo/lab5/models/Coordinates.java | 3 + app/src/main/java/itmo/lab5/models/Flat.java | 142 ++++++++++- app/src/main/java/itmo/lab5/models/House.java | 36 +++ .../java/itmo/lab5/models/enums/Furnish.java | 3 + .../itmo/lab5/models/enums/Transport.java | 3 + .../java/itmo/lab5/models/enums/View.java | 3 + .../main/java/itmo/lab5/parser/Writer.java | 50 ++++ 33 files changed, 1220 insertions(+), 440 deletions(-) create mode 100644 app/src/main/java/itmo/lab5/parser/Writer.java diff --git a/.project b/.project index 82c0761..7fc4a2b 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - lab5-test + lab5-lab5-itmo Project lab5-test created by Buildship. diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs index 69184a5..2a02054 100644 --- a/.settings/org.eclipse.buildship.core.prefs +++ b/.settings/org.eclipse.buildship.core.prefs @@ -1,2 +1,13 @@ -connection.project.dir=app +arguments=--init-script /home/oxff/.cache/jdtls/config/org.eclipse.osgi/58/0/.cp/gradle/init/init.gradle +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= eclipse.preferences.version=1 +gradle.user.home= +java.home=/usr/lib/jvm/java-21-openjdk +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/app/.classpath b/app/.classpath index be88c88..961b063 100644 --- a/app/.classpath +++ b/app/.classpath @@ -12,6 +12,13 @@ + + + + + + + diff --git a/app/.settings/org.eclipse.buildship.core.prefs b/app/.settings/org.eclipse.buildship.core.prefs index 0af630e..258eb47 100644 --- a/app/.settings/org.eclipse.buildship.core.prefs +++ b/app/.settings/org.eclipse.buildship.core.prefs @@ -1,13 +1,13 @@ -arguments=--init-script /home/oxff/.cache/jdtls/config/org.eclipse.osgi/58/0/.cp/gradle/init/init.gradle +arguments= auto.sync=false build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(LOCAL_INSTALLATION(/usr/share/java/gradle)) -connection.project.dir= +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir=.. eclipse.preferences.version=1 gradle.user.home= -java.home=/usr/lib/jvm/java-21-openjdk +java.home= jvm.arguments= offline.mode=false -override.workspace.settings=true -show.console.view=true -show.executions.view=true +override.workspace.settings=false +show.console.view=false +show.executions.view=false diff --git a/app/src/main/java/itmo/lab5/App.java b/app/src/main/java/itmo/lab5/App.java index 4fe4d77..08194ea 100644 --- a/app/src/main/java/itmo/lab5/App.java +++ b/app/src/main/java/itmo/lab5/App.java @@ -19,9 +19,19 @@ import itmo.lab5.models.Flat; import itmo.lab5.parser.Reader; import itmo.lab5.cli.helpers.History; +/** + * This class is an entry point of the application that manages a collection of + * {@code Flat} objects. + * The main method creates REPL that provide ability create or modificate data + */ public class App { private static final Logger LOGGER = Logger.getLogger(FileHandler.class.getName()); + /** + * The main method that serves as the entry point for the application. + * + * @param g_args command-line arguments (not used) + */ public static void main(String[] g_args) { Path dataFilePath = null; var history = new History(); @@ -37,7 +47,6 @@ public class App { .register("remove_key", new RemoveKeyCommand()) .register("history", new HistoryCommand()) .register("insert", new InsertCommand()) - .register("update", new UpdateCommand()) .register("save", new SaveCommand()) .register("execute_script", new ExecuteCommand()) .register("print_field_ascending_number_of_rooms", new FieldCommand()) @@ -51,7 +60,7 @@ public class App { dataFilePath = getDataFileFromEnv("LAB5_DATA"); flats = new Reader().parseCSV(dataFilePath.toFile()); } catch (IllegalArgumentException | IOException e) { - LOGGER.log(Level.WARNING, "There's error while loading file: " + e.getMessage()); + LOGGER.log(Level.WARNING, "There's error while loading file: {0}", e.getMessage()); return; } @@ -79,14 +88,22 @@ public class App { } } + /** + * Retrieves the path to the data file from the specified environment variable. + * + * @param envVariable the name of the environment variable that contains the + * file path + * @return the path to the data file + * @throws IOException if an I/O error happens + * @throws IllegalArgumentException if the env variable is not set or invalid + */ public static Path getDataFileFromEnv(String envVariable) throws IOException { String envPath = System.getenv(envVariable); final Path path; - if (envPath == null || envPath.trim().isEmpty()) { + if (envPath == null || envPath.trim().isEmpty()) throw new IllegalArgumentException( "Environment variable '" + envVariable + "' is not set or empty."); - } try { path = Paths.get(envPath); @@ -97,18 +114,15 @@ public class App { ex); } - if (!Files.exists(path)) { + if (!Files.exists(path)) throw new IllegalArgumentException("The file at path '" + path + "' does not exist."); - } - if (!Files.isRegularFile(path)) { + if (!Files.isRegularFile(path)) throw new IllegalArgumentException("The path '" + path + "' is not a file. Check twice!"); - } - if (!Files.isReadable(path)) { + if (!Files.isReadable(path)) throw new IllegalArgumentException("The file at path '" + path + "' is not readable. " + "Check file permissions!"); - } LOGGER.info("File '" + path + "' exists and is readable."); return path; diff --git a/app/src/main/java/itmo/lab5/cli/CommandBuilder.java b/app/src/main/java/itmo/lab5/cli/CommandBuilder.java index 4162c08..efb6def 100644 --- a/app/src/main/java/itmo/lab5/cli/CommandBuilder.java +++ b/app/src/main/java/itmo/lab5/cli/CommandBuilder.java @@ -2,18 +2,37 @@ package itmo.lab5.cli; import itmo.lab5.interfaces.Command; +/** + * This class is responsible for constructing a CommandRegistry + * which contains all commands + */ public class CommandBuilder { private final CommandRegistry registry; - + + /** + * Constructs a new instance, initializing an empty CommandRegistry. + */ public CommandBuilder() { this.registry = new CommandRegistry(); } + /** + * Registers a new command with the specified name in the command registry. + * + * @param name the name of the command to register + * @param newCommand the command instance to be registered + * @return the current instance for method chaining + */ public CommandBuilder register(String name, Command newCommand) { this.registry.register(name, newCommand); return this; } - + + /** + * Builds and returns the constructed CommandRegistry containing all registered commands. + * + * @return the constructed CommandRegistry + */ public CommandRegistry build() { return this.registry; } diff --git a/app/src/main/java/itmo/lab5/cli/CommandContext.java b/app/src/main/java/itmo/lab5/cli/CommandContext.java index c148920..715d631 100644 --- a/app/src/main/java/itmo/lab5/cli/CommandContext.java +++ b/app/src/main/java/itmo/lab5/cli/CommandContext.java @@ -2,13 +2,29 @@ package itmo.lab5.cli; import java.util.HashMap; +/** + * This class provides a context for storing and retrieving key-value pairs + */ public class CommandContext { private HashMap data = new HashMap<>(); + /** + * Stores a value associated with the specified key in the context. + * + * @param key the key under which the value is to be stored + * @param value the value to be stored in the context + */ public void set(String key, Object value) { this.data.put(key, value); } - + + /** + * Retrieves the value associated with the specified key from the context. + * + * @param name the key associated with value + * @return the value associated with the specified key, or {@code null} + * if the key does not exist + */ public Object get(String name) { return this.data.get(name); } diff --git a/app/src/main/java/itmo/lab5/cli/CommandInvoker.java b/app/src/main/java/itmo/lab5/cli/CommandInvoker.java index c33fa57..79ec5ef 100644 --- a/app/src/main/java/itmo/lab5/cli/CommandInvoker.java +++ b/app/src/main/java/itmo/lab5/cli/CommandInvoker.java @@ -3,17 +3,37 @@ package itmo.lab5.cli; import itmo.lab5.cli.helpers.History; import itmo.lab5.interfaces.*; +/** + * Invokes commands from a command registry and maintains command history. + */ public class CommandInvoker { + private final CommandRegistry registry; private final CommandContext context; private final History history; + /** + * Constructs a CommandInvoker with the specified command registry, + * context, and history. + * + * @param registry the command registry to retrieve commands from. + * @param context the context to be passed to commands during execution. + * @param history the history object to track executed commands. + */ public CommandInvoker(CommandRegistry registry, CommandContext context, History history) { this.registry = registry; this.context = context; this.history = history; } + /** + * Executes a command by its name with the provided arguments. + * + * @param commandName the name of the command to execute. + * @param args an array of arguments to pass to the command. + * @return the result of the command execution, or an error message if + * the command is unknown or an exception occurs during execution. + */ public String executeCommand(String commandName, String[] args) { Command command = registry.getByName(commandName); if (command != null) { diff --git a/app/src/main/java/itmo/lab5/cli/CommandRegistry.java b/app/src/main/java/itmo/lab5/cli/CommandRegistry.java index ad6ca39..4713e92 100644 --- a/app/src/main/java/itmo/lab5/cli/CommandRegistry.java +++ b/app/src/main/java/itmo/lab5/cli/CommandRegistry.java @@ -4,18 +4,41 @@ import java.util.HashMap; import java.util.Map; import itmo.lab5.interfaces.Command; +/** + * Manages the registration and retrieval of commands. + */ public class CommandRegistry { + private final Map commands = new HashMap<>(); + /** + * Registers a new command with the specified name. + * + * @param name the name of the command to register. + * @param newCommand the command instance to register. + */ public void register(String name, Command newCommand) { this.commands.put(name, newCommand); } + /** + * Retrieves a command by its name. + * + * @param name the name of the command to retrieve. + * @return the command associated with the specified name, or null + * if no command is found. + */ public Command getByName(String name) { return this.commands.get(name); } + /** + * Returns a map of all registered commands. + * + * @return a map where the keys are command names and the values are + * the corresponding command instances. + */ public Map getAllCommands() { return this.commands; } -} +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/cli/commands/ClearCommand.java b/app/src/main/java/itmo/lab5/cli/commands/ClearCommand.java index d7b11eb..7c132bb 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/ClearCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/ClearCommand.java @@ -6,18 +6,34 @@ import itmo.lab5.cli.CommandContext; import itmo.lab5.interfaces.Command; import itmo.lab5.models.Flat; +/** + * This class implements the Command interface and provides + * functionality to clear the collection of Flat objects. + * + * When executed, this command retrieves the collection from the command context + * and removes all items from it, effectively emptying the collection. + */ public class ClearCommand implements Command { - @Override - public String execute(String args[], CommandContext context) { - var collection = new HashMap(); + + /** + * Executes the clear command, removing all items from the collection + * of flats. + * + * @param args an array of arguments passed to the command + * @param context the command context that contains the collection of flats + * @return a message indicating the result of the operation, or an error message if the collection cannot be parsed + */ + @Override + public String execute(String args[], CommandContext context) { + var collection = new HashMap(); - try { - collection = (HashMap) context.get("collection"); - } catch (ClassCastException e) { - return "Can't clear collection. It might be missed"; + try { + collection = (HashMap) context.get("collection"); + } catch (ClassCastException e) { + return "Can't clear collection. It might be missed."; + } + + collection.clear(); + return "Collection was successfully cleared!"; } - - collection.clear(); - return "Collection was successfuly cleared!"; - } } diff --git a/app/src/main/java/itmo/lab5/cli/commands/ExecuteCommand.java b/app/src/main/java/itmo/lab5/cli/commands/ExecuteCommand.java index aa2eda7..a9e5cff 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/ExecuteCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/ExecuteCommand.java @@ -10,52 +10,68 @@ import itmo.lab5.cli.CommandInvoker; import itmo.lab5.cli.CommandContext; import itmo.lab5.interfaces.*; +/** + * This class implements the Command} interface and provides + * functionality to execute a script file containing a series of commands. + * + * When executed, this command reads commands from the specified script file and + * executes them sequentially. It also checks for recursive script execution + * to prevent infinite loops. + */ public class ExecuteCommand implements Command { - private static final Set executingScripts = new HashSet(); + private static final Set executingScripts = new HashSet(); - @Override - public String execute(String args[], CommandContext context) { - if (args.length < 1) { - return "Usage: execute_script "; - } - - String fileName = args[0]; - File scriptFile = new File(fileName); - - if (executingScripts.contains(scriptFile.getAbsolutePath())) { - return "Error: Recursive script execution detected for file: " + fileName; - } - - executingScripts.add(scriptFile.getAbsolutePath()); - - try (Scanner fileScanner = new Scanner(scriptFile)) { - CommandInvoker commandInvoker = (CommandInvoker) context.get("commandInvoker"); - StringBuilder output = new StringBuilder(); - - while (fileScanner.hasNextLine()) { - String line = fileScanner.nextLine().trim(); - if (line.isEmpty() || line.startsWith("#")) { - continue; + /** + * Executes the script command, running the commands specified in the given + * script file. + * + * @param args an array of arguments passed to the command, where the first element is expected to be the name of the script file + * @param context the command context that contains the command invoker + * @return a string containing the output of the executed commands, or an error message if the script file cannot be found or executed + */ + @Override + public String execute(String args[], CommandContext context) { + if (args.length < 1) { + return "Usage: execute_script "; } - try { - String[] parts = line.split(" ", 2); - String commandName = parts[0]; - String[] commandArgs = parts.length > 1 ? parts[1].split(" ") : new String[0]; + String fileName = args[0]; + File scriptFile = new File(fileName); - String result = commandInvoker.executeCommand(commandName, commandArgs); - output.append("> ").append(line).append("\n").append(result).append("\n"); - } catch (Exception e) { - output.append("Error executing command '").append(line).append("': ") - .append(e.getMessage()).append("\n"); + if (executingScripts.contains(scriptFile.getAbsolutePath())) { + return "Error: Recursive script execution detected for file: " + fileName; } - } - return output.toString(); - } catch (FileNotFoundException e) { - return "Error: Script file not found: " + fileName; - } finally { - executingScripts.remove(scriptFile.getAbsolutePath()); + executingScripts.add(scriptFile.getAbsolutePath()); + + try (Scanner fileScanner = new Scanner(scriptFile)) { + CommandInvoker commandInvoker = (CommandInvoker) context.get("commandInvoker"); + StringBuilder output = new StringBuilder(); + + while (fileScanner.hasNextLine()) { + String line = fileScanner.nextLine().trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + + try { + String[] parts = line.split(" ", 2); + String commandName = parts[0]; + String[] commandArgs = parts.length > 1 ? parts[1].split(" ") : new String[0]; + + String result = commandInvoker.executeCommand(commandName, commandArgs); + output.append("> ").append(line).append("\n").append(result).append("\n"); + } catch (Exception e) { + output.append("Error executing command '").append(line).append("': ") + .append(e.getMessage()).append("\n"); + } + } + + return output.toString(); + } catch (FileNotFoundException e) { + return "Error: Script file not found: " + fileName; + } finally { + executingScripts.remove(scriptFile.getAbsolutePath()); + } } - } } diff --git a/app/src/main/java/itmo/lab5/cli/commands/ExitCommand.java b/app/src/main/java/itmo/lab5/cli/commands/ExitCommand.java index a2562cd..b65b77e 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/ExitCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/ExitCommand.java @@ -3,10 +3,24 @@ package itmo.lab5.cli.commands; import itmo.lab5.cli.CommandContext; import itmo.lab5.interfaces.Command; +/** + * This class implements the Command interface and provides + * functionality to terminate the application. + * + * When called, main loop will immediately stop. + */ public class ExitCommand implements Command { - @Override - public String execute(String[] args, CommandContext context) { - System.exit(0); - return ""; - } -} + + /** + * Executes the exit command, terminating the application. + * + * @param args an array of arguments passed to the command + * @param context the command context that may contain additional data + * @return an empty string + */ + @Override + public String execute(String[] args, CommandContext context) { + System.exit(0); + return ""; + } +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/cli/commands/FieldCommand.java b/app/src/main/java/itmo/lab5/cli/commands/FieldCommand.java index 24133d9..c40d724 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/FieldCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/FieldCommand.java @@ -7,7 +7,25 @@ import itmo.lab5.models.Flat; import java.util.*; import java.util.stream.Collectors; +/** + * This class represents a command that processes a collection of Flat objects. + * This command sorts the flats by the number of rooms and prints the sorted + * entries. + * + * Implements the Command interface. + */ public class FieldCommand implements Command { + + /** + * Executes the command, sorting and displaying the flats from the provided + * collection. + * + * @param args an array of arguments passed to the command. + * @param context the context containing the collection of flats to be processed. + * @return a message indicating the result of the execution. If the collection is empty, + * it returns "Nothing to show!". If the collection cannot be parsed, it returns + * "Can't parse collection!". + */ @Override public String execute(String args[], CommandContext context) { var collection = new HashMap(); @@ -18,19 +36,18 @@ public class FieldCommand implements Command { return "Can't parse collection!"; } - if (collection.isEmpty() || collection.size() == 0) { + if (collection.isEmpty()) { return "Nothing to show!"; } - List> sortedEntries = collection.entrySet().stream() + var sortedEntries = collection.entrySet().stream() .sorted(Comparator.comparingInt(entry -> entry.getValue().getNumberOfRooms())) .collect(Collectors.toList()); - sortedEntries.forEach(entry -> { + sortedEntries.forEach(entry -> System.out.println("Key: " + entry.getKey() + - ", Rooms: " + entry.getValue().getNumberOfRooms()); - }); + ", Rooms: " + entry.getValue().getNumberOfRooms())); return ""; } -} +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/cli/commands/FilterCommand.java b/app/src/main/java/itmo/lab5/cli/commands/FilterCommand.java index 92c902b..8665b6e 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/FilterCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/FilterCommand.java @@ -8,9 +8,38 @@ import java.util.HashMap; import java.util.stream.Collectors; import java.util.*; +/** + * Represents a command that filters a collection of {@link Flat} objects + * based on a specified classificator and a threshold value. + * + * Implements the Command interface. + */ public class FilterCommand implements Command { private String classificator; + /** + * Constructs a FilterCommand with the specified classificator. + * + * @param classificator the type of filtering to apply ("less" or "greater"). + */ + public FilterCommand(String classificator) { + this.classificator = classificator; + } + + /** + * Executes the command, filtering and displaying the flats from the + * provided collection based on the threshold value. + * + * @param args an array of arguments where the first element is the + * threshold value for filtering. + * @param context the context containing the collection of flats to be + * processed. + * @return a message indicating the result of the execution. If no + * classificator is provided, it returns "There's no + * classificator provided!". If the collection cannot be parsed, + * it returns "Can't parse collection!". If the collection is + * empty or the threshold is null, it returns "Nothing to show!". + */ @Override public String execute(String args[], CommandContext context) { if (args.length < 1) @@ -22,36 +51,32 @@ public class FilterCommand implements Command { try { collection = (HashMap) context.get("collection"); - threshold = Integer.parseInt(args[0]); + threshold = Integer.valueOf(args[0]); } catch (ClassCastException e) { return "Can't parse collection!"; } - if (collection.isEmpty() || collection.size() == 0 || threshold == null) { + if (collection.isEmpty() || threshold == null) return "Nothing to show!"; - } final int finalThreshold = threshold; - if (classificator == "less") { + if (classificator.equals("less")) { result = collection.values().stream() - .filter(flat -> flat.getView() != null && flat.getView().ordinal() < finalThreshold) + .filter(flat -> flat.getView() != null && + flat.getView().ordinal() < finalThreshold) .sorted(Comparator.comparingInt(flat -> flat.getView().ordinal())) .collect(Collectors.toList()); } else { result = collection.values().stream() - .filter(flat -> flat.getView() != null && flat.getView().ordinal() > finalThreshold) + .filter(flat -> flat.getView() != null && + flat.getView().ordinal() > finalThreshold) .sorted(Comparator.comparingInt(flat -> flat.getView().ordinal())) .collect(Collectors.toList()); } - result.forEach(entry -> { - System.out.println(entry); - }); + result.forEach(entry -> System.out.println(entry)); return ""; } - - public FilterCommand(String classificator) { - this.classificator = classificator; - } } + diff --git a/app/src/main/java/itmo/lab5/cli/commands/HelpCommand.java b/app/src/main/java/itmo/lab5/cli/commands/HelpCommand.java index 8353e9c..0b9fb6e 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/HelpCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/HelpCommand.java @@ -4,25 +4,40 @@ import itmo.lab5.cli.CommandContext; import itmo.lab5.cli.CommandRegistry; import itmo.lab5.interfaces.Command; +/** + * This class implements the Command interface and provides + * functionality to display a list of available commands in the application. + * + * When executed, this command retrieves the command registry from the context + * and constructs a string that lists all registered commands. + * If no commands are available, it informs user. + */ public class HelpCommand implements Command { - @Override - public String execute(String args[], CommandContext context) { - var registry = (CommandRegistry) context.get("registry"); - StringBuilder result = new StringBuilder(); + + /** + * Executes the help command, returning a list of available commands. + * + * @param args an array of arguments passed to the command + * @param context the command context that contains the command registry + * @return a string listing all available commands, or a message indicating that no commands are available + */ + @Override + public String execute(String args[], CommandContext context) { + var registry = (CommandRegistry) context.get("registry"); + StringBuilder result = new StringBuilder(); - if (registry == null) { - result.append("There aren't any avaliable commands!"); - return result.toString(); + if (null == registry) { + result.append("There aren't any available commands!"); + return result.toString(); + } + + result.append("List of available commands: "); + registry.getAllCommands().keySet().forEach(command -> + result.append(command).append(", ")); + + return result + .delete(result.length() - 2, result.length()) + .append(".") + .toString(); } - - result.append("List of avaliable commands: "); - for (String command : registry.getAllCommands().keySet()) { - result.append(command + ", "); - } - - return result - .delete(result.length() - 2, result.length()) - .append(".") - .toString(); - } } diff --git a/app/src/main/java/itmo/lab5/cli/commands/HistoryCommand.java b/app/src/main/java/itmo/lab5/cli/commands/HistoryCommand.java index 0082a0f..687fe48 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/HistoryCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/HistoryCommand.java @@ -4,20 +4,37 @@ import itmo.lab5.cli.CommandContext; import itmo.lab5.interfaces.Command; import itmo.lab5.cli.helpers.*; +/** + * This class implements the Command interface and provides + * functionality to display the command history of the application. + * + * When executed, this command retrieves the history from the command context + * and returns a string representation of the command history. If the history is + * null or cannot be parsed, an appropriate error message is returned. + * + */ public class HistoryCommand implements Command { - @Override - public String execute(String args[], CommandContext context) { - var history = new History(); + + /** + * Executes the history command, returning latest 8 (by design). + * + * @param args an array of arguments passed to the command + * @param context the command context that contains the history of commands + * @return a string representation of the command history, or an error message if the history cannot be parsed + */ + @Override + public String execute(String args[], CommandContext context) { + var history = new History(); - try { - history = (History) context.get("history"); - } catch (ClassCastException e) { - return "There's problem with history. It might be null :("; + try { + history = (History) context.get("history"); + } catch (ClassCastException e) { + return "There's a problem with history. It might be null :("; + } + + if (null == history) + return "We reach the end of history. Somewhere in the world, one Fukuyama is rejoicing :_)"; + + return history.toString(); } - - if (history == null) - return "We reach End of History. Somewhere in the world, one Fukuyama is rejoicing :_)"; - - return history.toString(); - } -} +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/cli/commands/InfoCommand.java b/app/src/main/java/itmo/lab5/cli/commands/InfoCommand.java index d222c41..c30944f 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/InfoCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/InfoCommand.java @@ -5,26 +5,40 @@ import itmo.lab5.cli.CommandContext; import itmo.lab5.interfaces.Command; import itmo.lab5.models.Flat; +/** + * This class implements the Command interface and provides + * functionality to display information about the collection. + * + * When executed, this command retrieves the collection from the command context + * and returns details about the collection, including its type, the type of its + * items, and the number of items. + */ public class InfoCommand implements Command { - @Override - public String execute(String args[], CommandContext context) { - var flats = new HashMap(); + /** + * Executes the info command, returning information about the collection of + * flats. + * + * @param args an array of arguments passed to the command + * @param context the command context that contains the collection of flats + * @return a string containing information about the collection, or an error message if the collection cannot be parsed + */ + @Override + public String execute(String args[], CommandContext context) { + var flats = new HashMap(); - try { - flats = (HashMap) context.get("collection"); - } catch (ClassCastException e) { - return "Can't parse collection. Something goes wrong!"; + try { + flats = (HashMap) context.get("collection"); + } catch (ClassCastException e) { + return "Can't parse collection. Something goes wrong!"; + } + + if (null == flats || flats.isEmpty()) + return "Collection is empty now!"; + + var anyFlat = flats.values().iterator().next(); + + return ("Collections stores in: Information about collection: " + flats.getClass().getName() + "\n" + + "Collection consists of: " + anyFlat.getClass().getName() + "\n" + + "Items: " + flats.size()); } - - if (flats == null || flats.isEmpty()) { - return "Collection is empty now!"; - } - - var anyFlat = flats.values().iterator().next(); - - return ("Information about collection: \n" + - "Collections stores in: " + flats.getClass().getName() + "\n" + - "Collection consists of: " + anyFlat.getClass().getName() + "\n" + - "Items: " + flats.size()); - } -} +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/cli/commands/InsertCommand.java b/app/src/main/java/itmo/lab5/cli/commands/InsertCommand.java index eae7b16..d9d879d 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/InsertCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/InsertCommand.java @@ -1,10 +1,8 @@ package itmo.lab5.cli.commands; -import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Scanner; -import java.util.function.Function; import itmo.lab5.cli.helpers.*; import itmo.lab5.cli.CommandContext; @@ -12,84 +10,115 @@ import itmo.lab5.interfaces.Command; import itmo.lab5.models.enums.*; import itmo.lab5.models.*; +/** + * This class implements the Command interface and provides + * functionality to insert a new Flat object into the collection. + * + * This command can be executed in two modes: interactively, where the user is + * prompted for input, or with command-line arguments, where parameters are + * passed as key-value pairs. The command gathers all necessary information to + * create a new flat and adds it to the collection. + * + */ public class InsertCommand implements Command { - private final Scanner scanner = new Scanner(System.in); - private final ReaderUtil inputReader = new ReaderUtil(scanner); + private final Scanner scanner = new Scanner(System.in); + private final ReaderUtil inputReader = new ReaderUtil(scanner); - @Override - public String execute(String[] args, CommandContext context) { - if (args.length == 0) { - return executeInteractive(context); + /** + * Executes the insert command, either interactively or with provided + * arguments. + * + * @param args an array of arguments passed to the command; if empty, the command will execute interactively + * @param context the command context that contains the collection of flats + * @return a message indicating the result of the operation, or an error message if the collection cannot be parsed + */ + @Override + public String execute(String[] args, CommandContext context) { + if (args.length == 0) + return executeInteractive(context); + + return executeWithArgs(args, context); } - return executeWithArgs(args, context); - } + /** + * Executes the insert command interactively, prompting the user for input + * to create a new flat. + * + * @param context the command context that contains the collection of flats + * @return a message indicating the result of the operation + */ + private String executeInteractive(CommandContext context) { + Date creationDate = new Date(); + String name = inputReader.promptString("- Enter name: ", false, null); - private String executeInteractive(CommandContext context) { - Date creationDate = new Date(); - String name = inputReader.promptString("- Enter name: ", false, null); + System.out.println("- Coordinates "); + int x = inputReader.promptNumber("\t Enter x: ", Integer.MIN_VALUE, Integer.MAX_VALUE, Integer::parseInt, null); + Long y = inputReader.promptNumber("\t Enter y: ", Long.MIN_VALUE, Long.MAX_VALUE, Long::parseLong, null); + var coordinates = new Coordinates(x, y); - System.out.println("- Coordinates "); - int x = inputReader.promptNumber("\t Enter x: ", Integer.MIN_VALUE, Integer.MAX_VALUE, Integer::parseInt, null); - Long y = inputReader.promptNumber("\t Enter y: ", Long.MIN_VALUE, Long.MAX_VALUE, Long::parseLong, null); - var coordinates = new Coordinates(x, y); + Double area = inputReader.promptNumber("\t Enter square: ", 0.0, 626.0, Double::parseDouble, null); + int numberOfRooms = inputReader.promptNumber("\t Enter rooms count: ", 1, Integer.MAX_VALUE, Integer::parseInt, null); - Double area = inputReader.promptNumber("\t Enter square: ", 0.0, 626.0, Double::parseDouble, null); - int numberOfRooms = inputReader.promptNumber("\t Enter rooms count: ", 1, Integer.MAX_VALUE, Integer::parseInt, - null); + System.out.println("- Furnish"); + Furnish furnish = inputReader.promptEnum("\t Enter furnish type: ", Furnish.class, null); - System.out.println("- Furnish"); - Furnish furnish = inputReader.promptEnum("\t Enter furnish type: ", Furnish.class, null); + System.out.println("- View"); + View view = inputReader.promptEnumNullable("\t Enter view type: ", View.class, null); - System.out.println("- View"); - View view = inputReader.promptEnumNullable("\t Enter view type: ", View.class, null); + System.out.println("- Transport"); + Transport transport = inputReader.promptEnum("\t Enter transport type: ", Transport.class, null); - System.out.println("- Transport"); - Transport transport = inputReader.promptEnum("\t Enter transport type: ", Transport.class, null); + System.out.println("- House"); + System.out.print("\t Enter house name: "); + String houseName = scanner.nextLine().trim(); - System.out.println("- House"); - System.out.print("\t Enter house name: "); - String houseName = scanner.nextLine().trim(); + House house = null; - House house = null; + if (!houseName.isEmpty()) { + int year = inputReader.promptNumber("\t Enter house age: ", 1, 959, Integer::parseInt, null); + long floors = inputReader.promptNumber("\t Enter house floors count: ", 1L, 77L, Long::parseLong, null); + house = new House(houseName, year, floors); + } - if (!houseName.isEmpty()) { - int year = inputReader.promptNumber("\t Enter house age: ", 1, 959, Integer::parseInt, null); - long floors = inputReader.promptNumber("\t Enter house floors count: ", 1L, 77L, Long::parseLong, null); - house = new House(houseName, year, floors); + try { + HashMap collection = (HashMap) context.get("collection"); + int newID = collection.size() + 1; + Flat flat = new Flat( + newID, + name, + coordinates, + creationDate, + area, + numberOfRooms, + furnish, + view, + transport, + house); + + collection.put(newID, flat); + } catch (ClassCastException e) { + return "There's an error while trying to add new element. Collection is broken."; + } + + return "New flat was successfully inserted!"; } - try { - HashMap collection = (HashMap) context.get("collection"); - int newID = collection.size() + 1; - Flat flat = new Flat( - newID, - name, - coordinates, - creationDate, - area, - numberOfRooms, - furnish, - view, - transport, - house); - - collection.put(newID, flat); - } catch (ClassCastException e) { - return "There's an error while trying to add new element. Collection is broken."; - } - - return "New flat was successfully inserted!"; - } - + /** + * Executes the insert command with provided arguments, creating a new flat + * based on the key-value pairs. + * + * @param args an array of arguments passed to the command + * @param context the command context that contains the collection of flats + * @return a message indicating the result of the operation + */ private String executeWithArgs(String[] args, CommandContext context) { HashMap params = new HashMap<>(); for (String arg : args) { String[] parts = arg.split("=", 2); - if (parts.length == 2) { + + if (parts.length == 2) params.put(parts[0], parts[1]); - } } Date creationDate = new Date(); @@ -194,12 +223,12 @@ public class InsertCommand implements Command { public static > boolean isValidEnumValue(Class enumClass, String value) { if (value == null) return false; + for (E constant : enumClass.getEnumConstants()) { - if (constant.name().equals(value)) { + if (constant.name().equals(value)) return true; - } } return false; } -} +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/cli/commands/RemoveKeyCommand.java b/app/src/main/java/itmo/lab5/cli/commands/RemoveKeyCommand.java index d869d91..0b01cd0 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/RemoveKeyCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/RemoveKeyCommand.java @@ -6,34 +6,51 @@ import itmo.lab5.cli.CommandContext; import itmo.lab5.interfaces.Command; import itmo.lab5.models.Flat; +/** + * This class implements the Command interface and provides + * functionality to remove a object from the collection based on its ID. + * + * When executed, this command retrieves the collection from the command + * context, checks for the specified ID in the arguments, and removes + * the corresponding object if it exists. If the ID is invalid + * or the collection is empty, appropriate error messages are returned. + */ public class RemoveKeyCommand implements Command { - @Override - public String execute(String args[], CommandContext context) { - HashMap flats = new HashMap(); - Integer idToDelete = null; + + /** + * Executes the remove key command, deleting a flat by ID. + * + * @param args an array of arguments passed to the command, where the first element is expected to be the ID of the flat to remove + * @param context the command context that contains the collection of flats + * @return a message indicating the result of the operation, or an error message if the collection cannot be parsed or the ID is invalid + */ + @Override + public String execute(String args[], CommandContext context) { + HashMap flats = new HashMap<>(); + Integer idToDelete = null; - try { - flats = (HashMap) context.get("collection"); - } catch (ClassCastException e) { - return "There's a problem with collection parsing"; + try { + flats = (HashMap) context.get("collection"); + } catch (ClassCastException e) { + return "There's a problem with collection parsing."; + } + + if (flats == null) + return "Collection is empty. Can't delete anything :("; + + try { + idToDelete = Integer.valueOf(args[0]); + } catch (NumberFormatException e) { + return "You provided a mistyped argument!"; + } + + if (idToDelete < 0) + return "Check argument's value twice."; + + if (!flats.containsKey(idToDelete)) + return "Can't find flat with such ID."; + + flats.remove(idToDelete); + return "Successfully deleted flat!"; } - - if (flats == null) - return "Collection is empty. Can't delete anything :("; - - try { - idToDelete = Integer.parseInt(args[0]); - } catch (Exception e) { - return "You provide misstyped argument!"; - } - - if (idToDelete == null) - return "Check argument's value twice"; - - if (!flats.containsKey(idToDelete)) - return "Can't find flat with such id"; - - flats.remove(idToDelete); - return "Successfuly deleted flat!"; - } -} +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/cli/commands/ReplaceCommand.java b/app/src/main/java/itmo/lab5/cli/commands/ReplaceCommand.java index be87afc..ce11a32 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/ReplaceCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/ReplaceCommand.java @@ -2,50 +2,223 @@ package itmo.lab5.cli.commands; import itmo.lab5.cli.CommandContext; import itmo.lab5.interfaces.*; -import itmo.lab5.models.Flat; +import itmo.lab5.models.*; +import itmo.lab5.models.enums.*; +import java.util.Date; import java.util.HashMap; +/** + * Represents a command that replaces a flat in a collection based on a + * specified classificator ("greater" or "lower") and a given id. + * + * Implements the {@link Command} interface. + */ public class ReplaceCommand implements Command { - private String classificator; + + private final String classificator; + /** + * Constructs a ReplaceCommand with the specified classificator. + * + * @param classificator the type of comparison for replacement, must be + * either "greater" or "lower". + * @throws IllegalArgumentException if the classificator is not valid. + */ public ReplaceCommand(String classificator) { - this.classificator = classificator; + if (!classificator.equalsIgnoreCase("greater") && + !classificator.equalsIgnoreCase("lower")) { + throw new IllegalArgumentException("Classificator must be either " + + "'greater' or 'lower'"); + } + this.classificator = classificator.toLowerCase(); } + /** + * Executes the command, replacing a flat in the collection if the + * specified condition is met. + * + * @param args an array of arguments where the first element is the id + * of the flat to be replaced, followed by optional parameters + * for the new flat. + * + * @param context the context containing the collection of flats to be + * processed. + * + * @return a message indicating the result of the execution. If no + * arguments are provided, it returns "No arguments provided.". + * If the id format is invalid, it returns "Invalid id format.". + * If the collection cannot be parsed, it returns "Collection is + * corrupted or not found in context.". If the flat with the + * specified id does not exist, it returns "No element with id + * {id}". If the flat is successfully replaced, it returns a + * success message; otherwise, it indicates that the condition + * for replacement was not met. + */ @Override - public String execute(String args[], CommandContext context) { - var collection = new HashMap(); - int idToUpdate; + public String execute(String[] args, CommandContext context) { + if (args.length == 0) + return "No arguments provided."; + int id; + try { + id = Integer.parseInt(args[0]); + } catch (NumberFormatException e) { + return "Invalid id format."; + } + + HashMap collection; + try { + collection = (HashMap) context.get("collection"); + } catch (ClassCastException e) { + return "Collection is corrupted or not found in context."; + } + + if (!collection.containsKey(id)) + return "No element with id " + id; + + Flat oldFlat = collection.get(id); + Flat updatedFlat = buildFlatFromArgs(args, oldFlat); + + if (updatedFlat == null) + return "Failed to create a new flat from arguments."; + + int result = compareFlats(updatedFlat, oldFlat); + boolean shouldReplace = classificator.equals("greater") ? result > 0 : result < 0; + + if (shouldReplace) { + collection.put(id, updatedFlat); + return "Flat with id " + id + " was replaced."; + } else { + return "Flat was not replaced: condition not met."; + } + } + + /** + * Builds a new Flat object from the provided arguments, using the + * existing flat's properties as defaults. + * + * @param args an array of arguments containing the new flat's properties. + * @param oldFlat the existing flat to use as a reference for default values. + * @return a new Flat object or null if the creation fails. + */ + private Flat buildFlatFromArgs(String[] args, Flat oldFlat) { HashMap params = new HashMap<>(); - for (String arg : args) { String[] parts = arg.split("=", 2); + if (parts.length == 2) params.put(parts[0], parts[1]); } - if (args.length < 1) - return "Can't get value without id!"; - try { - collection = (HashMap) context.get("collection"); - } catch (ClassCastException e) { - return "Can't parse collection!"; - } + String name = params.getOrDefault("name", oldFlat.getName()); - try { - idToUpdate = Integer.parseInt(args[0]); + Coordinates coordinates = oldFlat.getCoordinates(); + if (params.containsKey("x") || params.containsKey("y")) { + int x = params.containsKey("x") ? Integer.parseInt(params.get("x")) : coordinates.getX(); + long y = params.containsKey("y") ? Long.parseLong(params.get("y")) : coordinates.getY(); + coordinates = new Coordinates(x, y); + } + + Double area = params.containsKey("area") ? Double.parseDouble(params.get("area")) : oldFlat.getArea(); + int numberOfRooms = params.containsKey("numberOfRooms") ? Integer.parseInt(params.get("numberOfRooms")) + : oldFlat.getNumberOfRooms(); + Furnish furnish = params.containsKey("furnish") ? Furnish.valueOf(params.get("furnish").toUpperCase()) + : oldFlat.getFurnish(); + View view = params.containsKey("view") ? View.valueOf(params.get("view").toUpperCase()) : oldFlat.getView(); + Transport transport = params.containsKey("transport") ? Transport.valueOf(params.get("transport").toUpperCase()) + : oldFlat.getTransport(); + + House house = oldFlat.getHouse(); + if (params.containsKey("name") || params.containsKey("year") || params.containsKey("floors")) { + String houseName = params.containsKey("name") ? params.get("name") : house.getName(); + int year = params.containsKey("year") ? Integer.parseInt(params.get("year")) : house.getYear(); + long floors = params.containsKey("houseFloors") ? Long.parseLong(params.get("houseFloors")) + : house.getNumberOfFloors(); + house = new House(houseName, year, floors); + } + + return new Flat( + oldFlat.getId(), + name, + coordinates, + new Date(), + area, + numberOfRooms, + furnish, + view, + transport, + house); } catch (NumberFormatException e) { - return "Can't parse provided id!"; + return null; + } + } + + /** + * Compares two Flat objects based on various attributes such as area, + * number of rooms, furnish type, view, transport, and house details. + * + * @param f1 the first flat to compare. + * @param f2 the second flat to compare. + * @return a negative integer, zero, or a positive integer as the first + * flat is less than, equal to, or greater than the second flat. + */ + private int compareFlats(Flat f1, Flat f2) { + int result = safeCompare(f1.getArea(), f2.getArea()); + if (result != 0) + return result; + + result = Integer.compare(f1.getNumberOfRooms(), f2.getNumberOfRooms()); + if (result != 0) + return result; + + result = safeCompare(f1.getFurnish(), f2.getFurnish()); + if (result != 0) + return result; + + result = safeCompare(f1.getView(), f2.getView()); + if (result != 0) + return result; + + result = safeCompare(f1.getTransport(), f2.getTransport()); + if (result != 0) + return result; + + House h1 = f1.getHouse(), h2 = f2.getHouse(); + if (h1 != null && h2 != null) { + result = Integer.compare(h1.getYear(), h2.getYear()); + if (result != 0) + return result; + + result = Long.compare(h1.getNumberOfFloors(), h2.getNumberOfFloors()); + if (result != 0) + return result; } - if (collection.isEmpty() || collection.size() == 0) - return "Nothing to show!"; - - var currentFlat = collection.get(idToUpdate); - - return ""; + return 0; } -} + + /** + * Safely compares two comparable objects, handling null values. + * + * @param a the first object to compare. + * @param b the second object to compare. + * @param the type of the objects being compared, which must extend + * Comparable. + * + * @return a negative integer, zero, or a positive integer as the first + * object is less than, equal to, or greater than the second + * object. If both are null, returns 0; if one is null, the + * other is considered greater. + */ + private > int safeCompare(T a, T b) { + if (a == null && b == null) + return 0; + if (a == null) + return -1; + if (b == null) + return 1; + return a.compareTo(b); + } +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/cli/commands/SaveCommand.java b/app/src/main/java/itmo/lab5/cli/commands/SaveCommand.java index 881018d..ef19113 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/SaveCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/SaveCommand.java @@ -3,71 +3,45 @@ package itmo.lab5.cli.commands; import itmo.lab5.interfaces.Command; import itmo.lab5.cli.*; import itmo.lab5.models.*; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.BufferedWriter; +import itmo.lab5.parser.Writer; import java.util.HashMap; -import java.text.SimpleDateFormat; +/** + * This class implements the Command interface and provides + * functionality to save the current collection of Flat objects to a specified + * file. + * + * When executed, this command retrieves the collection and the file path from + * the command context and uses a Writer instance to write the collection to the + * specified file. If the collection or file path cannot be retrieved, an + * appropriate error message is returned. + */ public class SaveCommand implements Command { - @Override - public String execute(String args[], CommandContext context) { - var collection = new HashMap(); - String filePath = null; - - try { - collection = (HashMap) context.get("collection"); - filePath = (String) context.get("path"); - } catch (ClassCastException e) { - return "Can't parse collection!"; - } - - if (filePath != null && collection != null) { - return saveCollectionToFile(collection, filePath); - } else { - return "Can't parse some object! Check filePath!"; - } - } - - static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - - public String saveCollectionToFile(HashMap flats, String filename) { - try (FileOutputStream fos = new FileOutputStream(filename); - OutputStreamWriter osw = new OutputStreamWriter(fos); - BufferedWriter writer = new BufferedWriter(osw)) { - - for (Flat flat : flats.values()) { - String date = ""; + + /** + * Executes the save command, saving the current collection of flats to a + * file. + * + * @param args an array of arguments passed to the command (not used in this command) + * @param context the command context that contains the collection of flats and the file path + * @return a message indicating the result of the save operation, or an error message if the collection or file path cannot be parsed + */ + @Override + public String execute(String args[], CommandContext context) { + var writer = new Writer(); + var collection = new HashMap(); + String filePath = null; try { - date = dateFormat.format(flat.getCreationDate()).toString(); - } catch (Exception e) { - System.out.println(e.getMessage()); + collection = (HashMap) context.get("collection"); + filePath = (String) context.get("path"); + } catch (ClassCastException e) { + return "Can't parse collection!"; } - String line = String.format("%d,%s,%d,%d,%s,%.2f,%d,%s,%s,%s,%s,%d,%d\n", - flat.getId(), - flat.getName(), - flat.getCoordinates().getX(), - flat.getCoordinates().getY(), - date, - flat.getArea(), - flat.getNumberOfRooms(), - flat.getFurnish(), - flat.getView() != null ? flat.getView() : "", - flat.getTransport(), - flat.getHouse() != null ? flat.getHouse().getName() : "", - flat.getHouse() != null ? flat.getHouse().getYear() : 0, - flat.getHouse() != null ? flat.getHouse().getNumberOfFloors() : 0); - writer.write(line); - } - - return "Collection has been saved to the file successfully."; - } catch (IOException e) { - e.printStackTrace(); - return "An error occurred while saving the collection to file."; + if (filePath != null && collection != null) + return writer.writeCollection(filePath, collection); + else + return "Can't parse some object! Check filePath!"; } - } } diff --git a/app/src/main/java/itmo/lab5/cli/commands/ShowCommand.java b/app/src/main/java/itmo/lab5/cli/commands/ShowCommand.java index 53b6f79..02b3b40 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/ShowCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/ShowCommand.java @@ -6,25 +6,40 @@ import itmo.lab5.models.Flat; import itmo.lab5.interfaces.Command; import itmo.lab5.cli.CommandContext; +/** + * This class implements the Command interface and provides + * functionality to display the contents of the collection of Flat objects. + * + * When executed, this command retrieves the collection from the command context + * and prints each Flat object to the standard output. If the collection is + * empty, it returns a message indicating that there is nothing to show. + */ public class ShowCommand implements Command { - @Override - public String execute(String args[], CommandContext context) { - var collection = new HashMap(); + + /** + * Executes the show command, displaying the contents of the collection + * of flats. + * + * @param args an array of arguments passed to the command + * @param context the command context that contains the collection of flats + * @return an empty string if the collection is displayed successfully, or an error message if the collection cannot be parsed + */ + @Override + public String execute(String args[], CommandContext context) { + var collection = new HashMap(); - try { - collection = (HashMap) context.get("collection"); - } catch (ClassCastException e) { - return "Can't parse collection!"; + try { + collection = (HashMap) context.get("collection"); + } catch (ClassCastException e) { + return "Can't parse collection!"; + } + + if (collection.isEmpty()) + return "Nothing to show!"; + + for (Flat flat : collection.values()) + System.out.println(flat); + + return ""; } - - if (collection.isEmpty() || collection.size() == 0) { - return "Nothing to show!"; - } - - for (Flat flat : collection.values()) { - System.out.println(flat); - } - - return ""; - } } diff --git a/app/src/main/java/itmo/lab5/cli/commands/UpdateCommand.java b/app/src/main/java/itmo/lab5/cli/commands/UpdateCommand.java index b4346c8..40ba288 100644 --- a/app/src/main/java/itmo/lab5/cli/commands/UpdateCommand.java +++ b/app/src/main/java/itmo/lab5/cli/commands/UpdateCommand.java @@ -8,89 +8,105 @@ import itmo.lab5.interfaces.Command; import itmo.lab5.models.enums.*; import itmo.lab5.models.*; +/** + * This class implements the Command interface and provides + * functionality to update an existing Flat object in the collection based on + * its ID. + * + * When executed, this command retrieves the flat with the specified ID from + * the collection, prompts the user for new values to update the flat's + * properties, and then saves the updated flat back to the collection. + * + */ public class UpdateCommand implements Command { - private final Scanner scanner = new Scanner(System.in); - private final ReaderUtil inputReader = new ReaderUtil(scanner); + private final Scanner scanner = new Scanner(System.in); + private final ReaderUtil inputReader = new ReaderUtil(scanner); - @Override - public String execute(String[] args, CommandContext context) { - if (args.length < 1) { - return "Usage: update id {element}"; + /** + * Executes the update command, updating the flat with the specified ID. + * + * @param args an array of arguments passed to the command, where the first element is expected to be the ID of the flat to update + * @param context the command context that contains the collection of flats + * @return a message indicating the result of the operation, or an error message if the ID is invalid or the collection cannot be parsed + */ + @Override + public String execute(String[] args, CommandContext context) { + if (args.length < 1) + return "Usage: update id {element}"; + + Map collection = null; + + int idToUpdate; + try { + idToUpdate = Integer.parseInt(args[0]); + } catch (NumberFormatException e) { + return "Invalid ID format."; + } + + try { + collection = (Map) context.get("collection"); + } catch (ClassCastException e) { + return "There's an error while trying to parse collection."; + } + + if (!collection.containsKey(idToUpdate)) + return "No flat with ID: " + idToUpdate; + + Flat oldFlat = collection.get(idToUpdate); + + System.out.println("Updating flat with ID: " + idToUpdate); + String name = inputReader.promptString("- Enter name:", false, oldFlat.getName()); + + System.out.println("- Coordinates:"); + int x = inputReader.promptNumber("\t Enter x:", Integer.MIN_VALUE, Integer.MAX_VALUE, Integer::parseInt, + oldFlat.getCoordinates().getX()); + Long y = inputReader.promptNumber("\t Enter y:", Long.MIN_VALUE, Long.MAX_VALUE, Long::parseLong, + oldFlat.getCoordinates().getY()); + Coordinates coordinates = new Coordinates(x, y); + + Double area = inputReader.promptNumber("\t Enter square:", 0.0, 626.0, Double::parseDouble, oldFlat.getArea()); + int rooms = inputReader.promptNumber("\t Enter rooms count:", 1, Integer.MAX_VALUE, Integer::parseInt, + oldFlat.getNumberOfRooms()); + + System.out.println("- Furnish:"); + Furnish furnish = inputReader.promptEnum("\t Enter furnish type:", Furnish.class, oldFlat.getFurnish()); + + System.out.println("- View:"); + View view = inputReader.promptEnumNullable("\t Enter view type:", View.class, oldFlat.getView()); + + System.out.println("- Transport:"); + Transport transport = inputReader.promptEnum("\t Enter transport type:", Transport.class, oldFlat.getTransport()); + + System.out.println("- House:"); + System.out.print("\t Enter house name: "); + String houseName = scanner.nextLine().trim(); + + House house = oldFlat.getHouse(); + if (!houseName.isEmpty()) { + int year = inputReader.promptNumber("\t Enter house age: ", 1, 959, Integer::parseInt, + (house != null ? house.getYear() : 1)); + + long floors = inputReader.promptNumber("\t Enter house floors count: ", 1L, 77L, Long::parseLong, + (house != null ? house.getNumberOfFloors() : 1L)); + + house = new House(houseName, year, floors); + } + + Date creationDate = new Date(); + + Flat updatedFlat = new Flat( + idToUpdate, + name, + coordinates, + creationDate, + area, + rooms, + furnish, + view, + transport, + house); + + collection.put(idToUpdate, updatedFlat); + return "Flat with ID " + idToUpdate + " was successfully updated."; } - - Map collection = null; - - int idToUpdate; - try { - idToUpdate = Integer.parseInt(args[0]); - } catch (NumberFormatException e) { - return "Invalid ID format."; - } - - try { - collection = (Map) context.get("collection"); - } catch (ClassCastException e) { - return "There's an error while trying to parse collection"; - } - - if (!collection.containsKey(idToUpdate)) - return "No flat with ID: " + idToUpdate; - - Flat oldFlat = collection.get(idToUpdate); - - System.out.println("Updating flat with ID: " + idToUpdate); - String name = inputReader.promptString("- Enter name:", false, oldFlat.getName()); - - System.out.println("- Coordinates:"); - int x = inputReader.promptNumber("\t Enter x:", Integer.MIN_VALUE, Integer.MAX_VALUE, Integer::parseInt, - oldFlat.getCoordinates().getX()); - Long y = inputReader.promptNumber("\t Enter y:", Long.MIN_VALUE, Long.MAX_VALUE, Long::parseLong, - oldFlat.getCoordinates().getY()); - Coordinates coordinates = new Coordinates(x, y); - - Double area = inputReader.promptNumber("\t Enter square:", 0.0, 626.0, Double::parseDouble, oldFlat.getArea()); - int rooms = inputReader.promptNumber("\t Enter rooms count:", 1, Integer.MAX_VALUE, Integer::parseInt, - oldFlat.getNumberOfRooms()); - - System.out.println("- Furnish:"); - Furnish furnish = inputReader.promptEnum("\t Enter furnish type:", Furnish.class, oldFlat.getFurnish()); - - System.out.println("- View:"); - View view = inputReader.promptEnumNullable("\t Enter view type:", View.class, oldFlat.getView()); - - System.out.println("- Transport:"); - Transport transport = inputReader.promptEnum("\t Enter transport type:", Transport.class, oldFlat.getTransport()); - - System.out.println("- House:"); - System.out.print("\t Enter house name: "); - String houseName = scanner.nextLine().trim(); - - House house = oldFlat.getHouse(); - if (!houseName.isEmpty()) { - int year = inputReader.promptNumber("\t Enter house age: ", 1, 959, Integer::parseInt, - (house != null ? house.getYear() : 1)); - - long floors = inputReader.promptNumber("\t Enter house floors count: ", 1L, 77L, Long::parseLong, - (house != null ? house.getNumberOfFloors() : 1L)); - - house = new House(houseName, year, floors); - } - - Date creationDate = new Date(); - - Flat updatedFlat = new Flat( - idToUpdate, - name, - coordinates, - creationDate, - area, - rooms, - furnish, - view, - transport, - house); - - collection.put(idToUpdate, updatedFlat); - return "Flat with ID " + idToUpdate + " was successfully updated."; - } } diff --git a/app/src/main/java/itmo/lab5/cli/helpers/History.java b/app/src/main/java/itmo/lab5/cli/helpers/History.java index 18e5656..2cdaf27 100644 --- a/app/src/main/java/itmo/lab5/cli/helpers/History.java +++ b/app/src/main/java/itmo/lab5/cli/helpers/History.java @@ -3,10 +3,20 @@ package itmo.lab5.cli.helpers; import java.util.ArrayDeque; import java.util.Deque; +/** + * Manages the history of executed commands, maintaining a fixed size. + */ public class History { + private static final int MAX_SIZE = 8; - private final Deque history = new ArrayDeque(MAX_SIZE); + private final Deque history = new ArrayDeque<>(MAX_SIZE); + /** + * Adds a command to the history. If the history exceeds the maximum + * size, the oldest command is removed. + * + * @param command the command to add to the history. + */ public void add(String command) { if (history.size() >= MAX_SIZE) history.removeFirst(); @@ -14,17 +24,36 @@ public class History { history.add(command); } + /** + * Retrieves a command from the history by its index. + * + * @param x the index of the command to retrieve. + * @return the command at the specified index, or an empty string + * if the index is out of bounds. + */ public String get(int x) { - if (x >= MAX_SIZE) + if (x >= MAX_SIZE || x < 0) return ""; return (String) history.toArray()[x]; } + /** + * Returns a string representation of the entire command history. + * + * @return a string listing all commands in the history. + */ public String get() { return history.toString(); } + /** + * Returns a formatted string representation of the command history. + * + * @return a string that lists all commands in the history with + * a prefix. + */ + @Override public String toString() { var builder = new StringBuilder("History: \n"); history.forEach(command -> builder.append(" - " + command + "\n")); diff --git a/app/src/main/java/itmo/lab5/cli/helpers/ReaderUtil.java b/app/src/main/java/itmo/lab5/cli/helpers/ReaderUtil.java index 4d64996..4657f53 100644 --- a/app/src/main/java/itmo/lab5/cli/helpers/ReaderUtil.java +++ b/app/src/main/java/itmo/lab5/cli/helpers/ReaderUtil.java @@ -3,13 +3,30 @@ package itmo.lab5.cli.helpers; import java.util.Arrays; import java.util.Scanner; +/** + * Utility class for reading user input with various prompt methods. + */ public class ReaderUtil { + private final Scanner scanner; + /** + * Constructs a ReaderUtil with the specified Scanner for input. + * + * @param scanner the Scanner to read user input. + */ public ReaderUtil(Scanner scanner) { this.scanner = scanner; } + /** + * Prompts the user for a string input, allowing for an optional old value. + * + * @param message the message to display to the user. + * @param allowEmpty whether to allow empty input. + * @param oldValue the previous value to display as a hint. + * @return the user input or the old value if input is empty. + */ public String promptString(String message, boolean allowEmpty, String oldValue) { while (true) { System.out.print(message); @@ -27,6 +44,15 @@ public class ReaderUtil { } } + /** + * Prompts the user to select an enum value from the specified options. + * + * @param message the message to display to the user. + * @param enumClass the class of the enum type. + * @param oldValue the previous value to display as a hint. + * @param the type of the enum. + * @return the selected enum value or the old value if input is empty. + */ public > T promptEnum(String message, Class enumClass, T oldValue) { while (true) { if (oldValue != null) @@ -34,8 +60,8 @@ public class ReaderUtil { System.out.print( message + "(options: " + - String.join(", ", Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).toList()) + - "): "); + String.join(", ", Arrays.stream(enumClass.getEnumConstants()) + .map(Enum::name).toList()) + "): "); String input = scanner.nextLine().trim(); @@ -46,10 +72,18 @@ public class ReaderUtil { } catch (IllegalArgumentException e) { System.out.println("Invalid name; Please, try again: "); } - } } + /** + * Prompts the user to select an enum value, allowing for a nullable option. + * + * @param message the message to display to the user. + * @param enumClass the class of the enum type. + * @param oldValue the previous value to display as a hint. + * @param the type of the enum. + * @return the selected enum value or the old value if input is empty. + */ public > T promptEnumNullable(String message, Class enumClass, T oldValue) { while (true) { if (oldValue != null) @@ -57,8 +91,8 @@ public class ReaderUtil { System.out.print( message + "(options: " + - String.join(", ", Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).toList()) + - "): "); + String.join(", ", Arrays.stream(enumClass.getEnumConstants()) + .map(Enum::name).toList()) + "): "); String input = scanner.nextLine().trim(); @@ -72,6 +106,17 @@ public class ReaderUtil { } } + /** + * Prompts the user for a number input within specified bounds. + * + * @param message the message to display to the user. + * @param min the minimum acceptable value. + * @param max the maximum acceptable value. + * @param parser the parser to convert input to the desired type. + * @param oldValue the previous value to display as a hint. + * @param the type of the number, which must be comparable. + * @return the user input as a number or the old value if input is empty. + */ public > T promptNumber(String message, T min, T max, Parser parser, T oldValue) { while (true) { System.out.print(message + " (" + oldValue + "): "); @@ -94,4 +139,4 @@ public class ReaderUtil { public interface Parser { T parse(String input) throws Exception; } -} +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/interfaces/Command.java b/app/src/main/java/itmo/lab5/interfaces/Command.java index 524dc59..5a04c60 100644 --- a/app/src/main/java/itmo/lab5/interfaces/Command.java +++ b/app/src/main/java/itmo/lab5/interfaces/Command.java @@ -2,6 +2,18 @@ package itmo.lab5.interfaces; import itmo.lab5.cli.CommandContext; +/** + * Represents a command that can be executed with a set of arguments + * and a command context. + */ public interface Command { + + /** + * Executes the command with the provided arguments and context. + * + * @param args an array of arguments to be used during execution. + * @param context the context in which the command is executed. + * @return a string representing the result of the command execution. + */ String execute(String[] args, CommandContext context); -} +} \ No newline at end of file diff --git a/app/src/main/java/itmo/lab5/models/Coordinates.java b/app/src/main/java/itmo/lab5/models/Coordinates.java index 4ad6f58..d90aca7 100644 --- a/app/src/main/java/itmo/lab5/models/Coordinates.java +++ b/app/src/main/java/itmo/lab5/models/Coordinates.java @@ -1,5 +1,8 @@ package itmo.lab5.models; +/** + * Represents a point in the provided task. + */ public class Coordinates { private int x; private Long y; // Поле не может быть null diff --git a/app/src/main/java/itmo/lab5/models/Flat.java b/app/src/main/java/itmo/lab5/models/Flat.java index 6da691d..e30f180 100644 --- a/app/src/main/java/itmo/lab5/models/Flat.java +++ b/app/src/main/java/itmo/lab5/models/Flat.java @@ -5,6 +5,9 @@ import itmo.lab5.models.enums.*; import java.text.SimpleDateFormat; import java.util.Date; +/** + * Represents a flat, provided by se.ifmo.ru (no 462025) + */ public class Flat { private int id; // Значение поля должно быть больше 0, Значение этого поля должно быть // уникальным, Значение этого поля должно генерироваться автоматически @@ -19,6 +22,20 @@ public class Flat { private Transport transport; // Поле не может быть null private House house; // Поле может быть null + /** + * Constructs a Flat object with the specified attributes. + * + * @param id the unique identifier of the flat (must be greater than 0). + * @param name the name of the flat (must not be null or empty). + * @param coordinates the coordinates of the flat (must not be null). + * @param creationDate the creation date of the flat (must not be null). + * @param area the area of the flat (must be greater than 0 and less than or equal to 626). + * @param numberOfRooms the number of rooms in the flat (must be greater than 0). + * @param furnish the furnish type of the flat (must not be null). + * @param view the view from the flat (can be null). + * @param transport the transport type associated with the flat (must not be null). + * @param house the house details associated with the flat (can be null). + */ public Flat(int id, String name, Coordinates coordinates, Date creationDate, Double area, int numberOfRooms, Furnish furnish, View view, Transport transport, House house) { this.id = id; @@ -32,91 +49,202 @@ public class Flat { this.transport = transport; this.house = house; } - + + /** + * Default constructor for creating an empty Flat object. + */ public Flat() { } + /** + * Gets the unique identifier of the flat. + * + * @return the id of the flat. + */ public int getId() { return id; } + /** + * Sets the unique identifier of the flat. + * + * @param id the new id of the flat (must be greater than 0). + */ public void setId(int id) { this.id = id; } - + + /** + * Gets the name of the flat. + * + * @return the name of the flat. + */ public String getName() { return name; } + /** + * Sets the name of the flat. + * + * @param name the new name of the flat (must not be null or empty). + */ public void setName(String name) { this.name = name; } + /** + * Gets the coordinates of the flat. + * + * @return the coordinates of the flat (must not be null). + */ public Coordinates getCoordinates() { return coordinates; } + /** + * Sets the coordinates of the flat. + * + * @param coordinates the new coordinates of the flat (must not be null). + */ public void setCoordinates(Coordinates coordinates) { this.coordinates = coordinates; } + /** + * Gets the creation date of the flat. + * + * @return the creation date of the flat (must not be null). + */ public Date getCreationDate() { return creationDate; } + /** + * Sets the creation date of the flat. + * + * @param creationDate the new creation date of the flat (must not be null). + */ public void setCreationDate(Date creationDate) { this.creationDate = creationDate; } - + + /** + * Gets the area of the flat. + * + * @return the area of the flat (must be greater than 0 and less than or equal to 626). + */ public Double getArea() { return area; } + /** + * Sets the area of the flat. + * + * @param area the new area of the flat (must be greater than 0 and less than or equal to 626). + */ public void setArea(Double area) { this.area = area; } - + + /** + * Gets the number of rooms in the flat. + * + * @return the number of rooms in the flat (must be greater than 0). + */ public int getNumberOfRooms() { return numberOfRooms; } + /** + * Sets the number of rooms in the flat. + * + * @param numberOfRooms the new number of rooms in the flat (must be greater than 0). + */ public void setNumberOfRooms(int numberOfRooms) { this.numberOfRooms = numberOfRooms; } + /** + * Gets the furnish type of the flat. + * + * @return the furnish type of the flat (must not be null). + */ public Furnish getFurnish() { return furnish; } + /** + * Sets the furnish type of the flat. + * + * @param furnish the new furnish type of the flat (must not be null). + */ public void setFurnish(Furnish furnish) { this.furnish = furnish; } + /** + * Gets the view from the flat. + * + * @return the view from the flat (can be null). + */ public View getView() { return view; } + /** + * Sets the view from the flat. + * + * @param view the new view from the flat (can be null). + */ public void setView(View view) { this.view = view; } + /** + * Gets the transport type associated with the flat. + * + * @return the transport type associated with this class + */ public Transport getTransport() { return transport; } - + + + /** + * Sets the transport associated with this flat. + * + * @param transport the transport to be set + */ public void setTransport(Transport transport) { this.transport = transport; } - + + /** + * Returns the house associated with this flat. + * + * @return the house + */ public House getHouse() { return house; } + /** + * Sets the house associated with this flat. + * + * @param house the house to be set + */ public void setHouse(House house) { this.house = house; } - + + /** + * Returns a string representation of the flat, including its ID, name, + * coordinates, creation date, area, number of rooms, furnishing status, + * view, transport, and house. + * + * @return a string representation of the flat + */ @Override public String toString() { var sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); diff --git a/app/src/main/java/itmo/lab5/models/House.java b/app/src/main/java/itmo/lab5/models/House.java index 8d63a1d..c596299 100644 --- a/app/src/main/java/itmo/lab5/models/House.java +++ b/app/src/main/java/itmo/lab5/models/House.java @@ -1,28 +1,64 @@ package itmo.lab5.models; + +/** + * Represents a house with a name, year of construction, and number of floors. + * The name cannot be null, the year must be greater than 0 and less than or equal to 959, + * and the number of floors must be greater than 0 and less than or equal to 77. + */ public class House { private String name; // Поле не может быть null private int year; // Максимальное значение поля: 959, Значение поля должно быть больше 0 private long numberOfFloors; // Максимальное значение поля: 77, Значение поля должно быть больше 0 + /** + * Constructs a House with the specified name, year, and number of floors. + * + * @param name the name of the house, must not be null + * @param year the year of construction, must be greater than 0 and less + * than or equal to 959 + * @param numberOfFloors the number of floors, must be greater than 0 and + * less than or equal to 77 + */ public House(String name, int year, long numberOfFloors) { this.name = name; this.year = year; this.numberOfFloors = numberOfFloors; } + /** + * Returns the name of the house. + * + * @return the name of the house + */ public String getName() { return this.name; } + /** + * Returns the year of construction of the house. + * + * @return the year of construction + */ public int getYear() { return this.year; } + /** + * Returns the number of floors in the house. + * + * @return the number of floors + */ public long getNumberOfFloors() { return this.numberOfFloors; } + /** + * Returns a string representation of the house, including its name, year of construction, + * and number of floors. + * + * @return a string representation of the house + */ public String toString() { return "House(" + "name = '" + name + diff --git a/app/src/main/java/itmo/lab5/models/enums/Furnish.java b/app/src/main/java/itmo/lab5/models/enums/Furnish.java index d0a8935..01a4816 100644 --- a/app/src/main/java/itmo/lab5/models/enums/Furnish.java +++ b/app/src/main/java/itmo/lab5/models/enums/Furnish.java @@ -1,5 +1,8 @@ package itmo.lab5.models.enums; +/** + * Represents the different types of furnishing available for a flat. + */ public enum Furnish { DESIGNER, FINE, diff --git a/app/src/main/java/itmo/lab5/models/enums/Transport.java b/app/src/main/java/itmo/lab5/models/enums/Transport.java index f7ccf2e..dd6c4c3 100644 --- a/app/src/main/java/itmo/lab5/models/enums/Transport.java +++ b/app/src/main/java/itmo/lab5/models/enums/Transport.java @@ -1,5 +1,8 @@ package itmo.lab5.models.enums; +/** + * Represents the different *strange* levels of transport + */ public enum Transport { FEW, NONE, diff --git a/app/src/main/java/itmo/lab5/models/enums/View.java b/app/src/main/java/itmo/lab5/models/enums/View.java index 4d2efa5..bbdce24 100644 --- a/app/src/main/java/itmo/lab5/models/enums/View.java +++ b/app/src/main/java/itmo/lab5/models/enums/View.java @@ -1,5 +1,8 @@ package itmo.lab5.models.enums; +/** + * Represents the different types of views available from a flat. + */ public enum View { STREET, PARK, diff --git a/app/src/main/java/itmo/lab5/parser/Writer.java b/app/src/main/java/itmo/lab5/parser/Writer.java new file mode 100644 index 0000000..6a770f1 --- /dev/null +++ b/app/src/main/java/itmo/lab5/parser/Writer.java @@ -0,0 +1,50 @@ +package itmo.lab5.parser; + +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.HashMap; + +import itmo.lab5.models.Flat; + +public class Writer { + static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + public String writeCollection(String filename, HashMap flats) { + try (FileOutputStream fos = new FileOutputStream(filename); + OutputStreamWriter osw = new OutputStreamWriter(fos); + BufferedWriter writer = new BufferedWriter(osw)) { + + for (Flat flat : flats.values()) { + String date = ""; + + try { + date = dateFormat.format(flat.getCreationDate()).toString(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + + String line = String.format("%d,%s,%d,%d,%s,%.2f,%d,%s,%s,%s,%s,%d,%d\n", + flat.getId(), + flat.getName(), + flat.getCoordinates().getX(), + flat.getCoordinates().getY(), + date, + flat.getArea(), + flat.getNumberOfRooms(), + flat.getFurnish(), + flat.getView() != null ? flat.getView() : "", + flat.getTransport(), + flat.getHouse() != null ? flat.getHouse().getName() : "", + flat.getHouse() != null ? flat.getHouse().getYear() : 0, + flat.getHouse() != null ? flat.getHouse().getNumberOfFloors() : 0); + + writer.write(line); + } + + return "Collection has been saved to the file successfully."; + } catch (IOException e) { + e.printStackTrace(); + return "An error occurred while saving the collection to file."; + } + } +}