We are the wild!

This commit is contained in:
2025-04-14 03:13:07 +03:00
parent 0b6db20e7b
commit 0356930efb
33 changed files with 1220 additions and 440 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>lab5-test</name> <name>lab5-lab5-itmo</name>
<comment>Project lab5-test created by Buildship.</comment> <comment>Project lab5-test created by Buildship.</comment>
<projects> <projects>
</projects> </projects>

View File

@ -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 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

View File

@ -12,6 +12,13 @@
<attribute name="gradle_used_by_scope" value="main,test"/> <attribute name="gradle_used_by_scope" value="main,test"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/java">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21/"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/> <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/> <classpathentry kind="output" path="bin/default"/>

View File

@ -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 auto.sync=false
build.scans.enabled=false build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(LOCAL_INSTALLATION(/usr/share/java/gradle)) connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir= connection.project.dir=..
eclipse.preferences.version=1 eclipse.preferences.version=1
gradle.user.home= gradle.user.home=
java.home=/usr/lib/jvm/java-21-openjdk java.home=
jvm.arguments= jvm.arguments=
offline.mode=false offline.mode=false
override.workspace.settings=true override.workspace.settings=false
show.console.view=true show.console.view=false
show.executions.view=true show.executions.view=false

View File

@ -19,9 +19,19 @@ import itmo.lab5.models.Flat;
import itmo.lab5.parser.Reader; import itmo.lab5.parser.Reader;
import itmo.lab5.cli.helpers.History; 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 { public class App {
private static final Logger LOGGER = Logger.getLogger(FileHandler.class.getName()); 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) { public static void main(String[] g_args) {
Path dataFilePath = null; Path dataFilePath = null;
var history = new History(); var history = new History();
@ -37,7 +47,6 @@ public class App {
.register("remove_key", new RemoveKeyCommand()) .register("remove_key", new RemoveKeyCommand())
.register("history", new HistoryCommand()) .register("history", new HistoryCommand())
.register("insert", new InsertCommand()) .register("insert", new InsertCommand())
.register("update", new UpdateCommand())
.register("save", new SaveCommand()) .register("save", new SaveCommand())
.register("execute_script", new ExecuteCommand()) .register("execute_script", new ExecuteCommand())
.register("print_field_ascending_number_of_rooms", new FieldCommand()) .register("print_field_ascending_number_of_rooms", new FieldCommand())
@ -51,7 +60,7 @@ public class App {
dataFilePath = getDataFileFromEnv("LAB5_DATA"); dataFilePath = getDataFileFromEnv("LAB5_DATA");
flats = new Reader().parseCSV(dataFilePath.toFile()); flats = new Reader().parseCSV(dataFilePath.toFile());
} catch (IllegalArgumentException | IOException e) { } 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; 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 { public static Path getDataFileFromEnv(String envVariable) throws IOException {
String envPath = System.getenv(envVariable); String envPath = System.getenv(envVariable);
final Path path; final Path path;
if (envPath == null || envPath.trim().isEmpty()) { if (envPath == null || envPath.trim().isEmpty())
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Environment variable '" + envVariable + "' is not set or empty."); "Environment variable '" + envVariable + "' is not set or empty.");
}
try { try {
path = Paths.get(envPath); path = Paths.get(envPath);
@ -97,18 +114,15 @@ public class App {
ex); ex);
} }
if (!Files.exists(path)) { if (!Files.exists(path))
throw new IllegalArgumentException("The file at path '" + path + "' does not exist."); 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!"); 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. " + throw new IllegalArgumentException("The file at path '" + path + "' is not readable. " +
"Check file permissions!"); "Check file permissions!");
}
LOGGER.info("File '" + path + "' exists and is readable."); LOGGER.info("File '" + path + "' exists and is readable.");
return path; return path;

View File

@ -2,18 +2,37 @@ package itmo.lab5.cli;
import itmo.lab5.interfaces.Command; import itmo.lab5.interfaces.Command;
/**
* This class is responsible for constructing a CommandRegistry
* which contains all commands
*/
public class CommandBuilder { public class CommandBuilder {
private final CommandRegistry registry; private final CommandRegistry registry;
/**
* Constructs a new instance, initializing an empty CommandRegistry.
*/
public CommandBuilder() { public CommandBuilder() {
this.registry = new CommandRegistry(); 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) { public CommandBuilder register(String name, Command newCommand) {
this.registry.register(name, newCommand); this.registry.register(name, newCommand);
return this; return this;
} }
/**
* Builds and returns the constructed CommandRegistry containing all registered commands.
*
* @return the constructed CommandRegistry
*/
public CommandRegistry build() { public CommandRegistry build() {
return this.registry; return this.registry;
} }

View File

@ -2,13 +2,29 @@ package itmo.lab5.cli;
import java.util.HashMap; import java.util.HashMap;
/**
* This class provides a context for storing and retrieving key-value pairs
*/
public class CommandContext { public class CommandContext {
private HashMap<String, Object> data = new HashMap<>(); private HashMap<String, Object> 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) { public void set(String key, Object value) {
this.data.put(key, 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) { public Object get(String name) {
return this.data.get(name); return this.data.get(name);
} }

View File

@ -3,17 +3,37 @@ package itmo.lab5.cli;
import itmo.lab5.cli.helpers.History; import itmo.lab5.cli.helpers.History;
import itmo.lab5.interfaces.*; import itmo.lab5.interfaces.*;
/**
* Invokes commands from a command registry and maintains command history.
*/
public class CommandInvoker { public class CommandInvoker {
private final CommandRegistry registry; private final CommandRegistry registry;
private final CommandContext context; private final CommandContext context;
private final History history; 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) { public CommandInvoker(CommandRegistry registry, CommandContext context, History history) {
this.registry = registry; this.registry = registry;
this.context = context; this.context = context;
this.history = history; 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) { public String executeCommand(String commandName, String[] args) {
Command command = registry.getByName(commandName); Command command = registry.getByName(commandName);
if (command != null) { if (command != null) {

View File

@ -4,17 +4,40 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import itmo.lab5.interfaces.Command; import itmo.lab5.interfaces.Command;
/**
* Manages the registration and retrieval of commands.
*/
public class CommandRegistry { public class CommandRegistry {
private final Map<String, Command> commands = new HashMap<>(); private final Map<String, Command> 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) { public void register(String name, Command newCommand) {
this.commands.put(name, 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) { public Command getByName(String name) {
return this.commands.get(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<String, Command> getAllCommands() { public Map<String, Command> getAllCommands() {
return this.commands; return this.commands;
} }

View File

@ -6,7 +6,23 @@ import itmo.lab5.cli.CommandContext;
import itmo.lab5.interfaces.Command; import itmo.lab5.interfaces.Command;
import itmo.lab5.models.Flat; 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 { public class ClearCommand implements Command {
/**
* 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
var collection = new HashMap<Integer, Flat>(); var collection = new HashMap<Integer, Flat>();
@ -14,10 +30,10 @@ public class ClearCommand implements Command {
try { try {
collection = (HashMap<Integer, Flat>) context.get("collection"); collection = (HashMap<Integer, Flat>) context.get("collection");
} catch (ClassCastException e) { } catch (ClassCastException e) {
return "Can't clear collection. It might be missed"; return "Can't clear collection. It might be missed.";
} }
collection.clear(); collection.clear();
return "Collection was successfuly cleared!"; return "Collection was successfully cleared!";
} }
} }

View File

@ -10,9 +10,25 @@ import itmo.lab5.cli.CommandInvoker;
import itmo.lab5.cli.CommandContext; import itmo.lab5.cli.CommandContext;
import itmo.lab5.interfaces.*; 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 { public class ExecuteCommand implements Command {
private static final Set<String> executingScripts = new HashSet<String>(); private static final Set<String> executingScripts = new HashSet<String>();
/**
* 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
if (args.length < 1) { if (args.length < 1) {

View File

@ -3,7 +3,21 @@ package itmo.lab5.cli.commands;
import itmo.lab5.cli.CommandContext; import itmo.lab5.cli.CommandContext;
import itmo.lab5.interfaces.Command; 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 { public class ExitCommand implements Command {
/**
* 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 @Override
public String execute(String[] args, CommandContext context) { public String execute(String[] args, CommandContext context) {
System.exit(0); System.exit(0);

View File

@ -7,7 +7,25 @@ import itmo.lab5.models.Flat;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; 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 { 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
var collection = new HashMap<Integer, Flat>(); var collection = new HashMap<Integer, Flat>();
@ -18,18 +36,17 @@ public class FieldCommand implements Command {
return "Can't parse collection!"; return "Can't parse collection!";
} }
if (collection.isEmpty() || collection.size() == 0) { if (collection.isEmpty()) {
return "Nothing to show!"; return "Nothing to show!";
} }
List<Map.Entry<Integer, Flat>> sortedEntries = collection.entrySet().stream() var sortedEntries = collection.entrySet().stream()
.sorted(Comparator.comparingInt(entry -> entry.getValue().getNumberOfRooms())) .sorted(Comparator.comparingInt(entry -> entry.getValue().getNumberOfRooms()))
.collect(Collectors.toList()); .collect(Collectors.toList());
sortedEntries.forEach(entry -> { sortedEntries.forEach(entry ->
System.out.println("Key: " + entry.getKey() + System.out.println("Key: " + entry.getKey() +
", Rooms: " + entry.getValue().getNumberOfRooms()); ", Rooms: " + entry.getValue().getNumberOfRooms()));
});
return ""; return "";
} }

View File

@ -8,9 +8,38 @@ import java.util.HashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.*; 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 { public class FilterCommand implements Command {
private String classificator; 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
if (args.length < 1) if (args.length < 1)
@ -22,36 +51,32 @@ public class FilterCommand implements Command {
try { try {
collection = (HashMap<Integer, Flat>) context.get("collection"); collection = (HashMap<Integer, Flat>) context.get("collection");
threshold = Integer.parseInt(args[0]); threshold = Integer.valueOf(args[0]);
} catch (ClassCastException e) { } catch (ClassCastException e) {
return "Can't parse collection!"; return "Can't parse collection!";
} }
if (collection.isEmpty() || collection.size() == 0 || threshold == null) { if (collection.isEmpty() || threshold == null)
return "Nothing to show!"; return "Nothing to show!";
}
final int finalThreshold = threshold; final int finalThreshold = threshold;
if (classificator == "less") { if (classificator.equals("less")) {
result = collection.values().stream() 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())) .sorted(Comparator.comparingInt(flat -> flat.getView().ordinal()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
result = collection.values().stream() 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())) .sorted(Comparator.comparingInt(flat -> flat.getView().ordinal()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
result.forEach(entry -> { result.forEach(entry -> System.out.println(entry));
System.out.println(entry);
});
return ""; return "";
} }
public FilterCommand(String classificator) {
this.classificator = classificator;
}
} }

View File

@ -4,21 +4,36 @@ import itmo.lab5.cli.CommandContext;
import itmo.lab5.cli.CommandRegistry; import itmo.lab5.cli.CommandRegistry;
import itmo.lab5.interfaces.Command; 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 { public class HelpCommand implements Command {
/**
* 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
var registry = (CommandRegistry) context.get("registry"); var registry = (CommandRegistry) context.get("registry");
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
if (registry == null) { if (null == registry) {
result.append("There aren't any avaliable commands!"); result.append("There aren't any available commands!");
return result.toString(); return result.toString();
} }
result.append("List of avaliable commands: "); result.append("List of available commands: ");
for (String command : registry.getAllCommands().keySet()) { registry.getAllCommands().keySet().forEach(command ->
result.append(command + ", "); result.append(command).append(", "));
}
return result return result
.delete(result.length() - 2, result.length()) .delete(result.length() - 2, result.length())

View File

@ -4,7 +4,24 @@ import itmo.lab5.cli.CommandContext;
import itmo.lab5.interfaces.Command; import itmo.lab5.interfaces.Command;
import itmo.lab5.cli.helpers.*; 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 { public class HistoryCommand implements Command {
/**
* 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
var history = new History(); var history = new History();
@ -12,11 +29,11 @@ public class HistoryCommand implements Command {
try { try {
history = (History) context.get("history"); history = (History) context.get("history");
} catch (ClassCastException e) { } catch (ClassCastException e) {
return "There's problem with history. It might be null :("; return "There's a problem with history. It might be null :(";
} }
if (history == null) if (null == history)
return "We reach End of History. Somewhere in the world, one Fukuyama is rejoicing :_)"; return "We reach the end of history. Somewhere in the world, one Fukuyama is rejoicing :_)";
return history.toString(); return history.toString();
} }

View File

@ -5,7 +5,23 @@ import itmo.lab5.cli.CommandContext;
import itmo.lab5.interfaces.Command; import itmo.lab5.interfaces.Command;
import itmo.lab5.models.Flat; 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 { public class InfoCommand implements Command {
/**
* 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
var flats = new HashMap<Integer, Flat>(); var flats = new HashMap<Integer, Flat>();
@ -16,14 +32,12 @@ public class InfoCommand implements Command {
return "Can't parse collection. Something goes wrong!"; return "Can't parse collection. Something goes wrong!";
} }
if (flats == null || flats.isEmpty()) { if (null == flats || flats.isEmpty())
return "Collection is empty now!"; return "Collection is empty now!";
}
var anyFlat = flats.values().iterator().next(); var anyFlat = flats.values().iterator().next();
return ("Information about collection: \n" + return ("Collections stores in: Information about collection: " + flats.getClass().getName() + "\n" +
"Collections stores in: " + flats.getClass().getName() + "\n" +
"Collection consists of: " + anyFlat.getClass().getName() + "\n" + "Collection consists of: " + anyFlat.getClass().getName() + "\n" +
"Items: " + flats.size()); "Items: " + flats.size());
} }

View File

@ -1,10 +1,8 @@
package itmo.lab5.cli.commands; package itmo.lab5.cli.commands;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Scanner; import java.util.Scanner;
import java.util.function.Function;
import itmo.lab5.cli.helpers.*; import itmo.lab5.cli.helpers.*;
import itmo.lab5.cli.CommandContext; import itmo.lab5.cli.CommandContext;
@ -12,19 +10,43 @@ import itmo.lab5.interfaces.Command;
import itmo.lab5.models.enums.*; import itmo.lab5.models.enums.*;
import itmo.lab5.models.*; 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 { public class InsertCommand implements Command {
private final Scanner scanner = new Scanner(System.in); private final Scanner scanner = new Scanner(System.in);
private final ReaderUtil inputReader = new ReaderUtil(scanner); private final ReaderUtil inputReader = new ReaderUtil(scanner);
/**
* 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 @Override
public String execute(String[] args, CommandContext context) { public String execute(String[] args, CommandContext context) {
if (args.length == 0) { if (args.length == 0)
return executeInteractive(context); 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) { private String executeInteractive(CommandContext context) {
Date creationDate = new Date(); Date creationDate = new Date();
String name = inputReader.promptString("- Enter name: ", false, null); String name = inputReader.promptString("- Enter name: ", false, null);
@ -35,8 +57,7 @@ public class InsertCommand implements Command {
var coordinates = new Coordinates(x, y); var coordinates = new Coordinates(x, y);
Double area = inputReader.promptNumber("\t Enter square: ", 0.0, 626.0, Double::parseDouble, 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, int numberOfRooms = inputReader.promptNumber("\t Enter rooms count: ", 1, Integer.MAX_VALUE, Integer::parseInt, null);
null);
System.out.println("- Furnish"); System.out.println("- Furnish");
Furnish furnish = inputReader.promptEnum("\t Enter furnish type: ", Furnish.class, null); Furnish furnish = inputReader.promptEnum("\t Enter furnish type: ", Furnish.class, null);
@ -82,15 +103,23 @@ public class InsertCommand implements Command {
return "New flat was successfully inserted!"; 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) { private String executeWithArgs(String[] args, CommandContext context) {
HashMap<String, String> params = new HashMap<>(); HashMap<String, String> params = new HashMap<>();
for (String arg : args) { for (String arg : args) {
String[] parts = arg.split("=", 2); String[] parts = arg.split("=", 2);
if (parts.length == 2) {
if (parts.length == 2)
params.put(parts[0], parts[1]); params.put(parts[0], parts[1]);
} }
}
Date creationDate = new Date(); Date creationDate = new Date();
String name = params.containsKey("name") ? params.get("name") String name = params.containsKey("name") ? params.get("name")
@ -194,11 +223,11 @@ public class InsertCommand implements Command {
public static <E extends Enum<E>> boolean isValidEnumValue(Class<E> enumClass, String value) { public static <E extends Enum<E>> boolean isValidEnumValue(Class<E> enumClass, String value) {
if (value == null) if (value == null)
return false; return false;
for (E constant : enumClass.getEnumConstants()) { for (E constant : enumClass.getEnumConstants()) {
if (constant.name().equals(value)) { if (constant.name().equals(value))
return true; return true;
} }
}
return false; return false;
} }

View File

@ -6,34 +6,51 @@ import itmo.lab5.cli.CommandContext;
import itmo.lab5.interfaces.Command; import itmo.lab5.interfaces.Command;
import itmo.lab5.models.Flat; 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 { public class RemoveKeyCommand implements Command {
/**
* 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
HashMap<Integer, Flat> flats = new HashMap<Integer, Flat>(); HashMap<Integer, Flat> flats = new HashMap<>();
Integer idToDelete = null; Integer idToDelete = null;
try { try {
flats = (HashMap<Integer, Flat>) context.get("collection"); flats = (HashMap<Integer, Flat>) context.get("collection");
} catch (ClassCastException e) { } catch (ClassCastException e) {
return "There's a problem with collection parsing"; return "There's a problem with collection parsing.";
} }
if (flats == null) if (flats == null)
return "Collection is empty. Can't delete anything :("; return "Collection is empty. Can't delete anything :(";
try { try {
idToDelete = Integer.parseInt(args[0]); idToDelete = Integer.valueOf(args[0]);
} catch (Exception e) { } catch (NumberFormatException e) {
return "You provide misstyped argument!"; return "You provided a mistyped argument!";
} }
if (idToDelete == null) if (idToDelete < 0)
return "Check argument's value twice"; return "Check argument's value twice.";
if (!flats.containsKey(idToDelete)) if (!flats.containsKey(idToDelete))
return "Can't find flat with such id"; return "Can't find flat with such ID.";
flats.remove(idToDelete); flats.remove(idToDelete);
return "Successfuly deleted flat!"; return "Successfully deleted flat!";
} }
} }

View File

@ -2,50 +2,223 @@ package itmo.lab5.cli.commands;
import itmo.lab5.cli.CommandContext; import itmo.lab5.cli.CommandContext;
import itmo.lab5.interfaces.*; 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; 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 { 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) { 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String[] args, CommandContext context) {
var collection = new HashMap<Integer, Flat>(); if (args.length == 0)
int idToUpdate; return "No arguments provided.";
int id;
try {
id = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
return "Invalid id format.";
}
HashMap<Integer, Flat> collection;
try {
collection = (HashMap<Integer, Flat>) 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<String, String> params = new HashMap<>(); HashMap<String, String> params = new HashMap<>();
for (String arg : args) { for (String arg : args) {
String[] parts = arg.split("=", 2); String[] parts = arg.split("=", 2);
if (parts.length == 2) if (parts.length == 2)
params.put(parts[0], parts[1]); params.put(parts[0], parts[1]);
} }
if (args.length < 1)
return "Can't get value without id!";
try { try {
collection = (HashMap<Integer, Flat>) context.get("collection"); String name = params.getOrDefault("name", oldFlat.getName());
} catch (ClassCastException e) {
return "Can't parse collection!"; 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);
} }
try { Double area = params.containsKey("area") ? Double.parseDouble(params.get("area")) : oldFlat.getArea();
idToUpdate = Integer.parseInt(args[0]); 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) { } catch (NumberFormatException e) {
return "Can't parse provided id!"; return null;
}
} }
if (collection.isEmpty() || collection.size() == 0) /**
return "Nothing to show!"; * 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;
var currentFlat = collection.get(idToUpdate); result = Integer.compare(f1.getNumberOfRooms(), f2.getNumberOfRooms());
if (result != 0)
return result;
return ""; 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;
}
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 <T> 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 <T extends Comparable<T>> 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);
} }
} }

View File

@ -3,17 +3,32 @@ package itmo.lab5.cli.commands;
import itmo.lab5.interfaces.Command; import itmo.lab5.interfaces.Command;
import itmo.lab5.cli.*; import itmo.lab5.cli.*;
import itmo.lab5.models.*; import itmo.lab5.models.*;
import itmo.lab5.parser.Writer;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
import java.util.HashMap; 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 { public class SaveCommand implements Command {
/**
* 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
var writer = new Writer();
var collection = new HashMap<Integer, Flat>(); var collection = new HashMap<Integer, Flat>();
String filePath = null; String filePath = null;
@ -24,50 +39,9 @@ public class SaveCommand implements Command {
return "Can't parse collection!"; return "Can't parse collection!";
} }
if (filePath != null && collection != null) { if (filePath != null && collection != null)
return saveCollectionToFile(collection, filePath); return writer.writeCollection(filePath, collection);
} else { else
return "Can't parse some object! Check filePath!"; 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<Integer, Flat> 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 = "";
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.";
}
}
} }

View File

@ -6,7 +6,24 @@ import itmo.lab5.models.Flat;
import itmo.lab5.interfaces.Command; import itmo.lab5.interfaces.Command;
import itmo.lab5.cli.CommandContext; 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 { public class ShowCommand implements Command {
/**
* 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 @Override
public String execute(String args[], CommandContext context) { public String execute(String args[], CommandContext context) {
var collection = new HashMap<Integer, Flat>(); var collection = new HashMap<Integer, Flat>();
@ -17,13 +34,11 @@ public class ShowCommand implements Command {
return "Can't parse collection!"; return "Can't parse collection!";
} }
if (collection.isEmpty() || collection.size() == 0) { if (collection.isEmpty())
return "Nothing to show!"; return "Nothing to show!";
}
for (Flat flat : collection.values()) { for (Flat flat : collection.values())
System.out.println(flat); System.out.println(flat);
}
return ""; return "";
} }

View File

@ -8,15 +8,31 @@ import itmo.lab5.interfaces.Command;
import itmo.lab5.models.enums.*; import itmo.lab5.models.enums.*;
import itmo.lab5.models.*; 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 { public class UpdateCommand implements Command {
private final Scanner scanner = new Scanner(System.in); private final Scanner scanner = new Scanner(System.in);
private final ReaderUtil inputReader = new ReaderUtil(scanner); private final ReaderUtil inputReader = new ReaderUtil(scanner);
/**
* 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 @Override
public String execute(String[] args, CommandContext context) { public String execute(String[] args, CommandContext context) {
if (args.length < 1) { if (args.length < 1)
return "Usage: update id {element}"; return "Usage: update id {element}";
}
Map<Integer, Flat> collection = null; Map<Integer, Flat> collection = null;
@ -30,7 +46,7 @@ public class UpdateCommand implements Command {
try { try {
collection = (Map<Integer, Flat>) context.get("collection"); collection = (Map<Integer, Flat>) context.get("collection");
} catch (ClassCastException e) { } catch (ClassCastException e) {
return "There's an error while trying to parse collection"; return "There's an error while trying to parse collection.";
} }
if (!collection.containsKey(idToUpdate)) if (!collection.containsKey(idToUpdate))

View File

@ -3,10 +3,20 @@ package itmo.lab5.cli.helpers;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
/**
* Manages the history of executed commands, maintaining a fixed size.
*/
public class History { public class History {
private static final int MAX_SIZE = 8;
private final Deque<String> history = new ArrayDeque<String>(MAX_SIZE);
private static final int MAX_SIZE = 8;
private final Deque<String> 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) { public void add(String command) {
if (history.size() >= MAX_SIZE) if (history.size() >= MAX_SIZE)
history.removeFirst(); history.removeFirst();
@ -14,17 +24,36 @@ public class History {
history.add(command); 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) { public String get(int x) {
if (x >= MAX_SIZE) if (x >= MAX_SIZE || x < 0)
return ""; return "";
return (String) history.toArray()[x]; 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() { public String get() {
return history.toString(); 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() { public String toString() {
var builder = new StringBuilder("History: \n"); var builder = new StringBuilder("History: \n");
history.forEach(command -> builder.append(" - " + command + "\n")); history.forEach(command -> builder.append(" - " + command + "\n"));

View File

@ -3,13 +3,30 @@ package itmo.lab5.cli.helpers;
import java.util.Arrays; import java.util.Arrays;
import java.util.Scanner; import java.util.Scanner;
/**
* Utility class for reading user input with various prompt methods.
*/
public class ReaderUtil { public class ReaderUtil {
private final Scanner scanner; 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) { public ReaderUtil(Scanner scanner) {
this.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) { public String promptString(String message, boolean allowEmpty, String oldValue) {
while (true) { while (true) {
System.out.print(message); 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 <T> the type of the enum.
* @return the selected enum value or the old value if input is empty.
*/
public <T extends Enum<T>> T promptEnum(String message, Class<T> enumClass, T oldValue) { public <T extends Enum<T>> T promptEnum(String message, Class<T> enumClass, T oldValue) {
while (true) { while (true) {
if (oldValue != null) if (oldValue != null)
@ -34,8 +60,8 @@ public class ReaderUtil {
System.out.print( System.out.print(
message + "(options: " + 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(); String input = scanner.nextLine().trim();
@ -46,10 +72,18 @@ public class ReaderUtil {
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
System.out.println("Invalid name; Please, try again: "); 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 <T> the type of the enum.
* @return the selected enum value or the old value if input is empty.
*/
public <T extends Enum<T>> T promptEnumNullable(String message, Class<T> enumClass, T oldValue) { public <T extends Enum<T>> T promptEnumNullable(String message, Class<T> enumClass, T oldValue) {
while (true) { while (true) {
if (oldValue != null) if (oldValue != null)
@ -57,8 +91,8 @@ public class ReaderUtil {
System.out.print( System.out.print(
message + "(options: " + 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(); 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 <T> 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 extends Comparable<T>> T promptNumber(String message, T min, T max, Parser<T> parser, T oldValue) { public <T extends Comparable<T>> T promptNumber(String message, T min, T max, Parser<T> parser, T oldValue) {
while (true) { while (true) {
System.out.print(message + " (" + oldValue + "): "); System.out.print(message + " (" + oldValue + "): ");

View File

@ -2,6 +2,18 @@ package itmo.lab5.interfaces;
import itmo.lab5.cli.CommandContext; import itmo.lab5.cli.CommandContext;
/**
* Represents a command that can be executed with a set of arguments
* and a command context.
*/
public interface Command { 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); String execute(String[] args, CommandContext context);
} }

View File

@ -1,5 +1,8 @@
package itmo.lab5.models; package itmo.lab5.models;
/**
* Represents a point in the provided task.
*/
public class Coordinates { public class Coordinates {
private int x; private int x;
private Long y; // Поле не может быть null private Long y; // Поле не может быть null

View File

@ -5,6 +5,9 @@ import itmo.lab5.models.enums.*;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
/**
* Represents a flat, provided by se.ifmo.ru (no 462025)
*/
public class Flat { public class Flat {
private int id; // Значение поля должно быть больше 0, Значение этого поля должно быть private int id; // Значение поля должно быть больше 0, Значение этого поля должно быть
// уникальным, Значение этого поля должно генерироваться автоматически // уникальным, Значение этого поля должно генерироваться автоматически
@ -19,6 +22,20 @@ public class Flat {
private Transport transport; // Поле не может быть null private Transport transport; // Поле не может быть null
private House house; // Поле может быть 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, public Flat(int id, String name, Coordinates coordinates, Date creationDate, Double area,
int numberOfRooms, Furnish furnish, View view, Transport transport, House house) { int numberOfRooms, Furnish furnish, View view, Transport transport, House house) {
this.id = id; this.id = id;
@ -33,90 +50,201 @@ public class Flat {
this.house = house; this.house = house;
} }
/**
* Default constructor for creating an empty Flat object.
*/
public Flat() { public Flat() {
} }
/**
* Gets the unique identifier of the flat.
*
* @return the id of the flat.
*/
public int getId() { public int getId() {
return id; 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) { public void setId(int id) {
this.id = id; this.id = id;
} }
/**
* Gets the name of the flat.
*
* @return the name of the flat.
*/
public String getName() { public String getName() {
return name; 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) { public void setName(String name) {
this.name = name; this.name = name;
} }
/**
* Gets the coordinates of the flat.
*
* @return the coordinates of the flat (must not be null).
*/
public Coordinates getCoordinates() { public Coordinates getCoordinates() {
return coordinates; 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) { public void setCoordinates(Coordinates coordinates) {
this.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() { public Date getCreationDate() {
return creationDate; 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) { public void setCreationDate(Date creationDate) {
this.creationDate = 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() { public Double getArea() {
return area; 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) { public void setArea(Double area) {
this.area = 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() { public int getNumberOfRooms() {
return numberOfRooms; 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) { public void setNumberOfRooms(int numberOfRooms) {
this.numberOfRooms = 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() { public Furnish getFurnish() {
return furnish; 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) { public void setFurnish(Furnish furnish) {
this.furnish = furnish; this.furnish = furnish;
} }
/**
* Gets the view from the flat.
*
* @return the view from the flat (can be null).
*/
public View getView() { public View getView() {
return view; return view;
} }
/**
* Sets the view from the flat.
*
* @param view the new view from the flat (can be null).
*/
public void setView(View view) { public void setView(View view) {
this.view = view; this.view = view;
} }
/**
* Gets the transport type associated with the flat.
*
* @return the transport type associated with this class
*/
public Transport getTransport() { public Transport getTransport() {
return transport; return transport;
} }
/**
* Sets the transport associated with this flat.
*
* @param transport the transport to be set
*/
public void setTransport(Transport transport) { public void setTransport(Transport transport) {
this.transport = transport; this.transport = transport;
} }
/**
* Returns the house associated with this flat.
*
* @return the house
*/
public House getHouse() { public House getHouse() {
return house; return house;
} }
/**
* Sets the house associated with this flat.
*
* @param house the house to be set
*/
public void setHouse(House house) { public void setHouse(House house) {
this.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 @Override
public String toString() { public String toString() {
var sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); var sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

View File

@ -1,28 +1,64 @@
package itmo.lab5.models; 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 { public class House {
private String name; // Поле не может быть null private String name; // Поле не может быть null
private int year; // Максимальное значение поля: 959, Значение поля должно быть больше 0 private int year; // Максимальное значение поля: 959, Значение поля должно быть больше 0
private long numberOfFloors; // Максимальное значение поля: 77, Значение поля должно быть больше 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) { public House(String name, int year, long numberOfFloors) {
this.name = name; this.name = name;
this.year = year; this.year = year;
this.numberOfFloors = numberOfFloors; this.numberOfFloors = numberOfFloors;
} }
/**
* Returns the name of the house.
*
* @return the name of the house
*/
public String getName() { public String getName() {
return this.name; return this.name;
} }
/**
* Returns the year of construction of the house.
*
* @return the year of construction
*/
public int getYear() { public int getYear() {
return this.year; return this.year;
} }
/**
* Returns the number of floors in the house.
*
* @return the number of floors
*/
public long getNumberOfFloors() { public long getNumberOfFloors() {
return this.numberOfFloors; 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() { public String toString() {
return "House(" + return "House(" +
"name = '" + name + "name = '" + name +

View File

@ -1,5 +1,8 @@
package itmo.lab5.models.enums; package itmo.lab5.models.enums;
/**
* Represents the different types of furnishing available for a flat.
*/
public enum Furnish { public enum Furnish {
DESIGNER, DESIGNER,
FINE, FINE,

View File

@ -1,5 +1,8 @@
package itmo.lab5.models.enums; package itmo.lab5.models.enums;
/**
* Represents the different *strange* levels of transport
*/
public enum Transport { public enum Transport {
FEW, FEW,
NONE, NONE,

View File

@ -1,5 +1,8 @@
package itmo.lab5.models.enums; package itmo.lab5.models.enums;
/**
* Represents the different types of views available from a flat.
*/
public enum View { public enum View {
STREET, STREET,
PARK, PARK,

View File

@ -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<Integer, Flat> 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.";
}
}
}