diff options
Added files from project 1 (mirror)
28 files changed, 2881 insertions, 0 deletions
diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Command.cpp b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Command.cpp new file mode 100644 index 0000000..c14bcc8 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Command.cpp @@ -0,0 +1,24 @@ +#include "Command.h" +#include <cstring> + +Command::Command() : Command("", nullptr) {} + +/*! \param nameInMenu C-style string, class stores at most 255 characters (last character is always terminating zero) + * \param exec Function pointer that will be executed when run() is called. Can be nullptr. + */ +Command::Command(const char* nameInMenu, void (*exec)()) { + strncpy(this->nameInMenu, nameInMenu, 255); + this->nameInMenu[255] = '\0'; + this->exec = exec; +} + +/*! Executes the stored function pointer, when it's not nullptr. Otherwise does nothing. + */ +void Command::run() const { + if (exec != nullptr) + exec(); +} + +const char* Command::get_nameInMenu() const { + return nameInMenu; +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Command.h b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Command.h new file mode 100644 index 0000000..f1eb55b --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Command.h @@ -0,0 +1,27 @@ +#ifndef HEADER_CONSOLEINTERFACE_COMMAND +#define HEADER_CONSOLEINTERFACE_COMMAND + +/*! \file Command.h + * \brief Stores the declaration of class Command + */ + +/*! \brief Stores a 256 character name and a function pointer to be executed when calling run() + * + * Menu uses this class for a more generic implementation of it's navigate function. + */ +class Command { + char nameInMenu[256]; + void (*exec)(); + +public: + //! Leaves name empty and exec function pointer to nullptr + Command(); + //! Copies contents of nameInMenu and stores exec + Command(const char* nameInMenu, void (*exec)()); + //! Executes the stored function pointer + void run() const; + + const char* get_nameInMenu() const; +}; + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Menu.cpp b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Menu.cpp new file mode 100644 index 0000000..3ab857e --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Menu.cpp @@ -0,0 +1,135 @@ +#include "Menu.h" +#include "Toolbox.hpp" +#include <cstring> + +Menu::Menu() : Menu("Menu", false, false) {} + +/*! \param title C-style string, class stores at most 255 characters (last character is always terminating zero) + * \param backExistsApp Flag whether or not to list the 0-th option as "Exit", rather than "Go Back" + * \param isSubMenu Flag whether or not to show the title as a subTitle or not + */ +Menu::Menu(const char* title, bool backExistsApp, bool isSubMenu) : menuOptions() { + this->backExistsApp = backExistsApp; + this->isSubMenu = isSubMenu; + strncpy(this->title, title, 255); + this->title[255] = '\0'; +} + +/*! \param title C-style string, class stores at most 255 characters (last character is always terminating zero) + * \param backExistsApp Flag whether or not to list the 0-th option as "Exit", rather than "Go Back" + * \param isSubMenu Flag whether or not to show the title as a subTitle or not + * \param commands Pointer to an array of Command instances + * \param commandCount Number of elements that "commands" points to + */ +Menu::Menu(const char* title, bool backExistsApp, bool isSubMenu, const Command* commands, unsigned commandCount) { + this->backExistsApp = backExistsApp; + this->isSubMenu = isSubMenu; + menuOptions = List<Command>(commands, commandCount); + strncpy(this->title, title, 255); + this->title[255] = '\0'; +} + +void Menu::addCommand(const Command& command) { + menuOptions.add(command); +} + +enum MessageType { + NoPrint, + Success, + Warning, + Error, +}; +const char* msg; +MessageType msgType; + +/*! \param message C-style string, saves to pointer for later reuse when showing the message. + * + * Stores the message pointer in a global variable (in the cpp file) and + * sets it's type as an Error in another global variable (in the cpp file). + * + * The next time the whole Menu is shown, the message will appear below the title. + */ +void Menu::registerError(const char* message) const { + msg = message; + msgType = Error; +} + +/*! \param message C-style string, saves to pointer for later reuse when showing the message. + * + * Stores the message pointer in a global variable (in the cpp file) and + * sets it's type as a Warning in another global variable (in the cpp file). + * + * The next time the whole Menu is shown, the message will appear below the title. + */ +void Menu::registerWarning(const char* message) const { + msg = message; + msgType = Warning; +} + +/*! \param message C-style string, saves to pointer for later reuse when showing the message. + * + * Stores the message pointer in a global variable (in the cpp file) and + * sets it's type as Success in another global variable (in the cpp file). + * + * The next time the whole Menu is shown, the message will appear below the title. + */ +void Menu::registerSuccess(const char* message) const { + msg = message; + msgType = Success; +} + +/*! + * Prints the title, then an Error/Warning/Sucess message (if set), + * prints all command names as an ordered list (starting from 1), + * waits for user input to select one of those commands (by list number) + * and finally executes the appropriate command's run() function. + * + * After the run() function exists, everything is reprinted. + * The 0 list index is always "Go Back"/"Exit" and it always stops the reprinting loop. + * + * If there are no commands, prints "Menu is empty!". + * If user input doesn't correspond to any command, registers an error message and reprints. + */ +void Menu::navigate() const { + if (menuOptions.get_count() == 0) { + printLine("Menu is empty!"); + return; + } + + unsigned buffer = 1; // We immedeately read the value of buffer, so this is fine + + while (buffer != 0) { + clear(); + if (isSubMenu) + subTitleBox(title); + else + titleBox(title); + + switch(msgType) { + case Error: errorBox(msg); msgType = NoPrint; break; + case Warning: warningBox(msg); msgType = NoPrint; break; + case Success: successBox(msg); msgType = NoPrint; break; + case NoPrint: printLine(""); break; + } + printLine(""); + + resetOrderedList(0); + if (backExistsApp) + printOrderedListElem("Exit"); + else + printOrderedListElem("Go Back"); + + for (unsigned i = 0; i < menuOptions.get_count(); i++) + printOrderedListElem(menuOptions[i].get_nameInMenu()); + + _printInputBoxLabel("Execute No [0-"); + print(menuOptions.get_count()); + print("]: "); + read(buffer); + + if (buffer > menuOptions.get_count()) + registerError("Invalid menu option!"); + else if (buffer > 0) + menuOptions[buffer - 1].run(); + } +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Menu.h b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Menu.h new file mode 100644 index 0000000..e0ea685 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Menu.h @@ -0,0 +1,46 @@ +#ifndef HEADER_CONSOLEINTERFACE_MENU +#define HEADER_CONSOLEINTERFACE_MENU + +/*! \file Menu.h + * \brief Stores the declaration of class Menu + */ + +#include "Command.h" +#include "../List/List.hpp" + +/*! \brief Handles navigation between multiple commands + * + * Shows the title, then below it an Error/Warning/Success message if set, + * lists all command options as an enumerated list, takes command index + * and calls the chosen command's run() function. + */ +class Menu { + char title[256]; + List<Command> menuOptions; + + bool backExistsApp; + bool isSubMenu; + +public: + //! Sets name as "Menu", leaves all flags to false and creates an empty Command list + Menu(); + //! Copies title, saves flags and creates and empty Command list + Menu(const char* title, bool backExistsApp, bool isSubMenu); + //! Copies title, saves flags and copies commands + Menu(const char* title, bool backExistsApp, bool isSubMenu, const Command* commands, unsigned commandCount); + + //! Adds a command to the internal Command list + void addCommand(const Command& command); + + //! Tell Menu to show an error message next time it prints + void registerError(const char* message) const; + //! Tell Menu to show a warning message next time it prints + void registerWarning(const char* message) const; + //! Tell Menu to show a success message next time it prints + void registerSuccess(const char* message) const; + + //! Lists commands and after user input executes an appropriate command run() function + void navigate() const; +}; + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Toolbox.cpp b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Toolbox.cpp new file mode 100644 index 0000000..ca83b3b --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Toolbox.cpp @@ -0,0 +1,223 @@ +#include "Toolbox.hpp" + +/*! + * Erases display and moves cursor to top left corner via ANSI escape sequences: \c ESC[2J \c ESC[1;1H + * + * \warning Not all terminals support any or all ANSI escape sequences + * + * \note Source: https://stackoverflow.com/a/52895729/12036073 + */ +void clear() { + std::cout << u8"\033[2J\033[1;1H"; +} + +/*! + * Sub boxes are like normal boxes, but indented with 4 spaces. + */ +void _printSubBoxSpacing() { + std::cout << " "; +} + +/* Title boxes */ + +/*! \param title C-style string, there are no size checks so it could wrap + * + * Prints the title, surrounded by "===". + */ +void titleBox(const char* title) { + std::cout << std::endl << "=== " << title << " ===" << std::endl; +} + +/*! \param title C-style string, there are no size checks so it could wrap + * + * Prints the title, surrounded by "---". + */ +void subTitleBox(const char* title) { + std::cout << std::endl << "--- " << title << " ---" << std::endl; +} + +/* Status boxes */ + +/*! \param message C-style string, there are no size checks so it could wrap + * + * Success boxes start with "<S>" and print the message. + * + * \remark Success/Warning/Error boxes start with "<C>" where C is an appropriate character. + */ +void successBox(const char* message) { + std::cout << "<S> " << message << std::endl; +} + +/*! + * \copydetails successBox() + * \sa _printSubBoxSpacing() + */ +void successSubBox(const char* message) { + _printSubBoxSpacing(); + successBox(message); +} + +/*! \param message C-style string, there are no size checks so it could wrap + * + * Warning boxes start with "<W>" and print the message. + * + * \remark Success/Warning/Error boxes start with "<C>" where C is an appropriate character. + */ +void warningBox(const char* message) { + std::cout << "<W> " << message << std::endl; +} + +/*! + * \copydetails warningBox() + * \sa _printSubBoxSpacing() + */ +void warningSubBox(const char* message) { + _printSubBoxSpacing(); + warningBox(message); +} + +/*! \param message C-style string, there are no size checks so it could wrap + * + * Error boxes start with "<E>" and print the message. + * + * \remark Success/Warning/Error boxes start with "<C>" where C is an appropriate character. + */ +void errorBox(const char* message) { + std::cout << "<E> " << message << std::endl; +} + +/*! + * \copydetails errorBox() + * \sa _printSubBoxSpacing() + */ +void errorSubBox(const char* message) { + _printSubBoxSpacing(); + errorBox(message); +} + +/* Input line boxes */ + +/*! \param label C-style string, there are no size checks so it could wrap + * + * Prints "(+)" and then the given label. + * It doesn't create spacing after label, so it should exist in the label itself. + * + * \remark Semi-internal means the function is defined in the header out of necessity (for templated functions) + */ +void _printInputBoxLabel(const char* label) { + std::cout << "(+) " << label; +} + +void __inputLineBoxGetInput(char* output, unsigned maxWidth, bool ignore) { + if (ignore) + std::cin.ignore(); + std::cin.getline(output, maxWidth); +} + +/*! \param label C-style string, there are no size check so it could wrap + * \param output Pointer to a char array + * \param maxWidth Maximum count of characters to read from user input. output MUST be able to hold that many characters! + * \param ignore Whether or not to ignore the first new-line delimiter. + * true by default, should be set to false only when an inputLineBox/SubBox has been issued directly prior or when it's the very first issued box command. + * + * Prints "(+)" before the label. + */ +void inputLineBox(const char* label, char* output, unsigned maxWidth, bool ignore) { + _printInputBoxLabel(label); + __inputLineBoxGetInput(output, maxWidth, ignore); +} + +/*! + * \copydetails inputLineBox() + * \sa _printSubBoxSpacing() + */ +void inputLineSubBox(const char* label, char* output, unsigned maxWidth, bool ignore) { + _printSubBoxSpacing(); + _printInputBoxLabel(label); + __inputLineBoxGetInput(output, maxWidth, ignore); +} + +/*! \param ignore Whether or not to ignore the first new-line delimiter. + * true by default, should be set to false only when an inputLineBox/SubBox has been issued directly prior or when it's the very first issued box command. + * + * Prints a "Press enter to continue" message and waits for the user + */ +void pressEnterToContinue(bool ignore) { + char tmp; + inputLineBox("[Press enter to continue]", &tmp, 1, ignore); +} + +/* Ordered list */ + +int orderedListIndex = 1; + +/*! + * Prints the latests ordered list index, surrounded by square brackets, and increments the ordered list index. + * + * \remark There are no size checks, so list index could overflow. + * + * \remark Semi-internal means the function is defined in the header out of necessity (for templated functions) + */ + +void _printOrderedListBeginning() { + std::cout << "[" << orderedListIndex++ << "] "; +} + +/*! \param starter Sets the starting number by which ordered list will enumerate + * + * \remark Although starter can be a negative number, ordered list will always increment up the number. + */ +void resetOrderedList(int starter) { + orderedListIndex = starter; +} + +/* Table */ + +void __printTableHeaderNumber(unsigned number) { + std::cout << ":" << ((number < 100) ? " " : "") << number << ((number < 10) ? " " : ""); +} + +void __printTableColumnHeader(unsigned start, unsigned count) { + std::cout << ": "; + for (unsigned i = 0; i < count; i++) + __printTableHeaderNumber(start++); + std::cout << ":" << std::endl; + + for (unsigned i = 0; i <= count; i++) + std::cout << "===="; + std::cout << "=" << std::endl; +} + +void __printTableLine(unsigned columns) { + std::cout << ":==="; + for (unsigned i = 0; i < columns; i++) + std::cout << "+---"; + std::cout << ":" << std::endl; +} + +/*! \param startNumber The number by which column and row enumeration begins + * \param columns How many columns the table should have + * \param items C-style string, each cell is a single character from the string + * + * Prints a string as a grid/table of characters from top to bottom, left to right. + * The first character is on the top left, the last one is on the bottom right. + * + * Table is printed until a terminating zero is encountered. + * The rows are "calculated" from the columns count and the items length. + */ +void table(unsigned startNumber, unsigned columns, const char* items) { + __printTableColumnHeader(startNumber, columns); + + __printTableHeaderNumber(startNumber); + for (unsigned i = 0, colInd = 0, rowInd = 0; items[i] != '\0'; i++, (++colInd) %= columns) { + if (colInd == 0 && i != 0) { + std::cout << ":" << std::endl; + __printTableLine(columns); + __printTableHeaderNumber((++rowInd) + startNumber); + } + + std::cout << ((colInd == 0) ? "║ " : "| ") << items[rowInd * columns + colInd] << " "; + } + std::cout << ":" << std::endl; +} + diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Toolbox.hpp b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Toolbox.hpp new file mode 100644 index 0000000..91306cb --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/ConsoleInterface/Toolbox.hpp @@ -0,0 +1,115 @@ +#ifndef HEADER_CONSOLEINTERFACE_TOOLBOX +#define HEADER_CONSOLEINTERFACE_TOOLBOX + +#include <iostream> + +/*! \file Toolbox.hpp + * \brief Stores a wide range of functions for simpler/more automated printing. + * + * Adds a lot of functions for printing. + * + * \remark iostream is included by necessity (templated functions), you should only use the provided functions, if you can. + */ + +#define MAX_LINE_WIDTH 1024 + +//! Clears the console screen +void clear(); +//! Prints a title box +void titleBox(const char* title); +//! Prints a title sub box +void subTitleBox(const char* title); + +//! Prints a success box +void successBox(const char* message); +//! Prints a success box as a sub box +void successSubBox(const char* message); +//! Prints a warning box +void warningBox(const char* message); +//! Prints a warning box as a sub box +void warningSubBox(const char* message); +//! Prints an error box +void errorBox(const char* message); +//! Prints an error box as a sub box +void errorSubBox(const char* message); + +//! Prints label, gets a whole line of input and stores it to output +void inputLineBox(const char* label, char* output, unsigned maxWidth, bool ignore = true); +//! Prints label as a sub box, gets a whole line of input and stores it to output +void inputLineSubBox(const char* label, char* output, unsigned maxWidth, bool ignore = true); +//! Waits for user to press enter +void pressEnterToContinue(bool ignore = true); + +//! Resets the ordered list starting number +void resetOrderedList(int starter = 1); + +//! Prints a string as a table +void table(unsigned startNumber, unsigned columns, const char* items); + +//! Semi-internal function that prints the spacing for sub boxes +void _printSubBoxSpacing(); +//! Semi-internal function that prints the label of an input box +void _printInputBoxLabel(const char* label); +//! Semi-internal function that prints the latest ordered list index +void _printOrderedListBeginning(); + +//! Reads user input and stores it +template <typename T> +void read(T* storage) { + std::cin >> storage; +} + +//! Reads user input and stores it +template <typename T> +void read(T& storage) { + std::cin >> storage; +} + +//! Prints given item +template <typename T> +void print(const T* item) { + std::cout << item; +} + +//! Prints given item +template <typename T> +void print(const T& item) { + std::cout << item; +} + +//! Prints given item and an endline character +template <typename T> +void printLine(const T* item) { + std::cout << item << std::endl; +} + +//! Prints given item and an endline character +template <typename T> +void printLine(const T& item) { + std::cout << item << std::endl; +} + +//! Prints the element as the latest list item +template <typename T> +void printOrderedListElem(const T& elem) { + _printOrderedListBeginning(); + printLine(elem); +} + +//! Prints a label and then reads user input and stores it +template <typename T> +void inputBox(const char* label, T* output) { + _printInputBoxLabel(label); + std::cin >> *output; +} + +//! Prints a label with sub box spacing and then reads user input and stores it +//! \sa _printSubBoxSpacing() +template <typename T> +void inputSubBox(const char* label, T* output) { + _printSubBoxSpacing(); + _printInputBoxLabel(label); + std::cin >> *output; +} + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/Date/Date.cpp b/Year 1/Object-oriented Programming/Project 1/src/Generic/Date/Date.cpp new file mode 100644 index 0000000..7c8f4d9 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/Date/Date.cpp @@ -0,0 +1,174 @@ +#include "Date.h" +#include <cstring> + +/* Private */ + +bool Date::isLeapYear() { + // Implements https://en.wikipedia.org/wiki/Leap_year#Gregorian_calendar + return year % 400 == 0 || year % 100 != 0 || year % 4 == 0; +} + +unsigned short Date::daysInMonth() { + if (month == 2) + return 28 + isLeapYear(); + if (month <= 7) + return 30 + month % 2; + return 31 - month % 2; +} + +/* Public */ + +Date::Date() : Date(01, 05, 2022) {} + +/*! \param str C-style string + * + * Values are initialized with zeroes. Then it parses the string: + * + * Skips spaces, saves day with atoi, skips two characters, + * skips spaces, saves month with atoi, skips two characters, + * skips spaces, saves year with atoi. + * + * Values are validated with setters. + * You should always have spaces between day, month and year and no non-numeric characters between them. + * + * \remark If there are non-numeric characters, as per atoi() spec, the appropriate property is zero. + */ +Date::Date(const char* str) : Date(0, 0, 0) { + while (*str == ' ' && *str != '\0') str++; + set_day(atoi(str)); + str += 2; + while (*str == ' ' && *str != '\0') str++; + set_month(atoi(str)); + str += 2; + while (*str == ' ' && *str != '\0') str++; + set_year(atoi(str)); +} + +/*! + * Initializes values with zeroes and then uses setters to save the function arguments. + */ +Date::Date(unsigned short day, unsigned short month, unsigned short year) { + this->day = this->month = this->year = 0; + set_day(day); + set_month(month); + set_year(year); +} + +unsigned short Date::get_day() const { + return day; +} + +/*! \return Whether newDay was saved or not + * + * If newDay is equal to 0, or is more than the appropriate days in the month, the day isn't set. + */ +bool Date::set_day(unsigned short newDay) { + if (newDay == 0 || newDay > daysInMonth()) + return false; + day = newDay; + return true; +} + +unsigned short Date::get_month() const { + return month; +} + +/*! \return Whether newMonth was saved or not + * + * If newMonth is equal to 0 or is more than 12, the month isn't set. + */ +bool Date::set_month(unsigned short newMonth) { + if (newMonth == 0 || newMonth > 12) + return false; + month = newMonth; + return true; +} + +unsigned short Date::get_year() const { + return year; +} + +/*! \return Whether year was saved or not + * + * Year isn't set when newYear is zero + */ +bool Date::set_year(unsigned short newYear) { + if (newYear == 0) + return false; + year = newYear; + return true; +} + +int digitToChar(unsigned digit) { + if (digit <= 9) + return '0' + digit; + return ' '; +} + +/*! \return String object in the format "day.month.year" + * + * \note Currently there isn't any way to change the formatting + */ +String Date::createString() const { + String out; + return ((((out += day) += ".") += month) += ".") += year; +} + +/*! \param istr An input stream + * + * Directly reads bytes from stream (calls read() function). + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ifstream + */ +void Date::read(std::istream& istr) { + istr.read((char*)&day, sizeof(day)); + istr.read((char*)&month, sizeof(month)); + istr.read((char*)&year, sizeof(year)); +} + +/*! \param ostr An output stream + * + * Directly writes bytes to stream (calls write() function). + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ofstream + */ +void Date::write(std::ostream& ostr) { + ostr.write((char*)&day, sizeof(day)); + ostr.write((char*)&month, sizeof(month)); + ostr.write((char*)&year, sizeof(year)); +} + +/*! \returns -1 if this < other, 1 if this > other, 0 if this == other + */ +int Date::compare(const Date& other) const { + if (year < other.year || month < other.month || day < other.day) + return -1; + return year > other.year || month > other.month || day > other.day; +} + + +/* Outside of class */ + +/*! + * Uses the stream's >> operator to read and parse the day, month and year. + * + * \note Best used with std::cin or text std::ifstream + */ +std::istream& operator>>(std::istream& istr, Date& dt) { + unsigned short d, M, y; + istr >> d >> M >> y; + dt = Date(d, M, y); + return istr; +} + +/*! + * Uses the stream's << operator to write the date in the format "day.month.year" + * + * \note Best used with std::cout or text std::ofstream + */ +std::ostream& operator<<(std::ostream& ostr, const Date& dt) { + ostr << dt.get_day() << "." << dt.get_month() << "." << dt.get_year(); + return ostr; +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/Date/Date.h b/Year 1/Object-oriented Programming/Project 1/src/Generic/Date/Date.h new file mode 100644 index 0000000..89cc89c --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/Date/Date.h @@ -0,0 +1,59 @@ +#ifndef HEADER_DATE +#define HEADER_DATE + +/*! \file Date.h + * \brief Stores the declaration of class Date + */ + +#include <istream> +#include <ostream> +#include "../String/String.h" + +/*! \brief Stores a day, month and year + */ +class Date { + unsigned short day; + unsigned short month; + unsigned short year; + + bool isLeapYear(); + unsigned short daysInMonth(); + +public: + //! Sets date as 01 May 2022 + Date(); + //! Parses string + Date(const char* str); + //! Sets day, month and year + Date(unsigned short day, unsigned short month, unsigned short year); + + //! Returns the day + unsigned short get_day() const; + //! Sets the day + bool set_day(unsigned short day); + //! Returns the month + unsigned short get_month() const; + //! Sets the month + bool set_month(unsigned short month); + //! Returns the year + unsigned short get_year() const; + //! Sets the year + bool set_year(unsigned short year); + + //! Returns the date as a String object + String createString() const; + + //! Reads date from stream + void read(std::istream& istr); + //! Writes date to stream + void write(std::ostream& ostr); + //! Compares two dates + int compare(const Date& other) const; +}; + +//! Reads date from stream with >> operator +std::istream& operator>>(std::istream& istr, Date& dt); +//! Writes date to stream with << operator +std::ostream& operator<<(std::ostream& ostr, const Date& dt); + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/List/List.hpp b/Year 1/Object-oriented Programming/Project 1/src/Generic/List/List.hpp new file mode 100644 index 0000000..0b53033 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/List/List.hpp @@ -0,0 +1,345 @@ +#ifndef HEADER_LIST +#define HEADER_LIST + +/*! \file List.hpp + * \brief Stores declaration and definition of templated class List + */ + +#include <istream> +#include <ostream> + +/*! \brief Templated class that stores an array of elements in dynamic memory + * + * \warning findIndex(), contain(), read(), write(), operator<<() and operator>>() require the type to have defined a couple of functions. + * \sa findIndex() + * \sa contain() + * \sa read() + * \sa write() + * \sa operator<<() + * \sa operator>>() + */ +template <typename T> +class List { +protected: + T* elements; + unsigned length; + unsigned count; + + void resize(); + void free(); + void copyFrom(const List& other); + +public: + //! Copies all elements in given array + List(const T* elements, unsigned elementsCount); + //! Adds an element + void add(const T& element); + //! Inserts element at given index + void insertAt(const T& element, unsigned index); + //! Removes element at index + bool removeAt(unsigned index); + //! Finds the index of element + unsigned findIndex(const T& element) const; + bool contain(const T& element) const; + //! Returns reference to element at index + T& operator[](unsigned index); + //! Returns constant reference to element at index + const T& operator[](unsigned index) const; + + //! Appends elements from other list + List<T>& operator+=(const List<T> other); + + //! Reads from stream + std::istream& read(std::istream& istr); + //! Writes to stream + std::ostream& write(std::ostream& ostr) const; + + //! Returns the length + unsigned get_length() const; + //! Returns the count + unsigned get_count() const; + + List(); + List& operator=(const List& other); + List(const List& other); + ~List(); + + List(List&& other); + List& operator=(List&& other); +}; + +//! Reads List from stream with >> operator +template <typename T> +std::istream& operator>>(std::istream& istr, List<T>& obj); +//! Writes List to stream with << operator +template <typename T> +std::ostream& operator<<(std::ostream& ostr, const List<T>& obj); + +/* Private */ + +template <typename T> +void List<T>::resize() { + length = (length == 0) ? 8 : length << 1; + T* temp = new T[length]; + for (int i = 0; i < count; i++) + temp[i] = elements[i]; + delete[] elements; + elements = temp; +} + +template <typename T> +void List<T>::free() { + delete[] elements; +} + +template <typename T> +void List<T>::copyFrom(const List& other) { + elements = new T[other.length]; + for (int i = 0; i < other.count; i++) + elements[i] = other.elements[i]; + length = other.length; + count = other.count; +} + +/* Public */ + +template <typename T> +List<T>::List(const T* elements, unsigned elementsCount) { + length = 8; + count = elementsCount; + this->elements = new T[length]; + for (unsigned i = 0; i < count; i++) + this->elements[i] = elements[i]; +} + +/*! + * Resizes internal array if there is no space for additional elements. + */ +template <typename T> +void List<T>::add(const T& element) { + if (length == count) resize(); + + elements[count++] = element; +} + +/*! + * If index is after the last element, the element is just added. + * Otherwise all elements after the index are shifted right and element is put in place. + * + * Resizes internal array if there is no space for additional elements. + */ +template <typename T> +void List<T>::insertAt(const T& element, unsigned index) { + if (index >= count) { + add(element); + return; + } + if (length == count) resize(); + + for (unsigned i = count; i > index; i--) + elements[i] = elements[i - 1]; + + elements[index] = element; + count++; +} + +/*! \returns Wether element could be removed + * + * If index is after that of the last element, nothing is done and false is returned. + * Otherwise elements after index are shifted right and count is reduced. + */ +template <typename T> +bool List<T>::removeAt(unsigned index) { + if (index >= count) return false; + + for (int i = index; i < count; i++) + elements[i] = elements[i + 1]; + count--; + return true; +} + +/*! \returns Index of element. If element isn't found, returns the count of element. + * + * \warning The function depends on the type having a function "compare" defined, which takes two elements and returns a number <0 if elem1 < elem2, >0 if elem1 > elem2, 0 if elem1 == elem2 + * \note Searching is done linearly + */ +template <typename T> +unsigned List<T>::findIndex(const T& element) const { + unsigned ind = 0; + while (ind < count && elements[ind].compare(element) != 0) + ind++; + return ind; +} + +/*! \returns Whether the element is contained in the current List + * + * \warning Function depends on findIndex(), which means the same "compare" function must be defined in the type + * \sa findIndex() + */ +template <typename T> +bool List<T>::contain(const T& element) const { + return findIndex(element) < count; +} + +template <typename T> +T& List<T>::operator[](unsigned index) { + return elements[index]; +} + +template <typename T> +const T& List<T>::operator[](unsigned index) const { + return elements[index]; +} + +template <typename T> +List<T>& List<T>::operator+=(const List<T> other) { + for (unsigned i = 0; i < other.length; i++) + add(other[i]); + return *this; +} + +/*! \param istr An input stream + * + * Directly reads bytes from stream (calls read() function). + * Any stored values are deleted and replaced with those from the stream. + * + * \warning The function depends on the type having a function "read" defined, which takes an std::istream& and writes it's data to it. Return type doesn't matter. + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ifstream + */ +template <typename T> +std::istream& List<T>::read(std::istream& istr) { + istr.read((char*)&length, sizeof(length)); + istr.read((char*)&count, sizeof(count)); + + delete[] elements; + elements = new T[length]; + + for (int i = 0; i < count; i++) + elements[i].read(istr); + + return istr; +} + +/*! \param ostr An output stream + * + * Directly writes bytes to stream (calls write() function). + * + * \warning The function depends on the type having a function "write" defined, which takes an std::ostream& and writes it's data to it. Return type doesn't matter. + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ofstream + */ +template <typename T> +std::ostream& List<T>::write(std::ostream& ostr) const { + ostr.write((const char*)&length, sizeof(length)); + ostr.write((const char*)&count, sizeof(count)); + + for (int i = 0; i < count; i++) + elements[i].write(ostr); + + return ostr; +} + +/*! + * Length is the size of the underlying array (allocated memory). + */ +template <typename T> +unsigned List<T>::get_length() const { + return length; +} + +/*! + * Count is the amount of elements that are stored. + */ +template <typename T> +unsigned List<T>::get_count() const { + return count; +} + +// Rule of 4 + +template <typename T> +List<T>::List() : List(nullptr, 0) {} + +template <typename T> +List<T>& List<T>::operator=(const List& other) { + if (this != &other) { + free(); + copyFrom(other); + } + return *this; +} + +template <typename T> +List<T>::List(const List& other) { + copyFrom(other); +} + +template <typename T> +List<T>::~List() { + free(); +} + +// Move semantics + +template <typename T> +List<T>::List(List&& other) { + length = other.length; + count = other.count; + elements = other.elements; + other.elements = nullptr; +} + +template <typename T> +List<T>& List<T>::operator=(List&& other) { + if (this != &other) { + free(); + length = other.length; + count = other.count; + elements = other.elements; + other.elements = nullptr; + } + return *this; +} + +/* Outside of class */ + +/*! + * Uses the stream's >> operator to read and parse the elements. + * The first item in the stream should be the count. + * + * \warning The function depends on the type having the operator >> defined, which takes an std::istream& and writes it's data to it. Return type doesn't matter. + * \note Best used with std::cin or text std::ifstream + */ +template <typename T> +std::istream& operator>>(std::istream& istr, List<T>& obj) { + List<T> newObj; + unsigned count; + istr >> count; + + T temp; + for (int i = 0; i < count; i++) { + istr >> temp; + obj.add(temp); + } + + return istr; +} + +/*! + * Uses the stream's << operator to write the count and then all objects. + * + * \warning The function depends on the type having the operator << defined, which takes an std::ostream& and writes it's data to it. Return type doesn't matter. + * \note Best used with std::cout or text std::ofstream + */ +template <typename T> +std::ostream& operator<<(std::ostream& ostr, const List<T>& obj) { + ostr << obj.get_count() << std::endl; + for (int i = 0; i < obj.get_count(); i++) + ostr << obj[i]; + + return ostr; +} + + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/List/OrderedList.hpp b/Year 1/Object-oriented Programming/Project 1/src/Generic/List/OrderedList.hpp new file mode 100644 index 0000000..8d13a5d --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/List/OrderedList.hpp @@ -0,0 +1,55 @@ +#ifndef HEADER_ORDEREDLIST +#define HEADER_ORDEREDLIST + +/*! \file OrderedList.hpp + * \brief Stores declaration and definition of templated class OrderedList + */ + +#include "List.hpp" + +/*! \brief Inherits List, but contains it's elements in a sorted manner + * + * \warning Outside those defined in List, insert() requires the type to have defined a "compare" of functions. + * \sa insert() + */ +template <typename T> +class OrderedList : public List<T> { +public: + OrderedList(); + OrderedList(const T* elements, unsigned elementsCount); + + /*! \brief \warning Function is deleted, use insert() instead + * \sa insert() + */ + void add(T element) = delete; + /*! \brief \warning Function is deleted, use insert() instead + * \sa insert() + */ + void insertAt(T element, unsigned index) = delete; + + //! Inserts an element at a sorted position + void insert(T element); +}; + +template <typename T> +OrderedList<T>::OrderedList() : OrderedList(nullptr, 0) {} + +template <typename T> +OrderedList<T>::OrderedList(const T* elements, unsigned elementsCount) : List<T>::List(elements, elementsCount) {} + +/*! + * \warning The function depends on the type having a function "compare" defined, which takes two elements and returns a number <0 if elem1 < elem2, >0 if elem1 > elem2, 0 if elem1 == elem2 + * \note Insertion is done linearly + */ +template <typename T> +void OrderedList<T>::insert(T element) { + if (List<T>::length == List<T>::count) List<T>::resize(); + + unsigned insertionInd = 0; + while (insertionInd < List<T>::count && List<T>::elements[insertionInd].compare(element) < 0) + insertionInd++; + + List<T>::insertAt(element, insertionInd); +} + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/String/String.cpp b/Year 1/Object-oriented Programming/Project 1/src/Generic/String/String.cpp new file mode 100644 index 0000000..9d61999 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/String/String.cpp @@ -0,0 +1,169 @@ +#include "String.h" +#include <cstring> + +/* Private */ + +void String::free() { + delete[] str; +} + +void String::copyFrom(const String& other) { + str = new char[other.length + 1]; + strncpy(str, other.str, other.length + 1); + length = other.length; +} + +/* Public */ + +/*! + * Allocated with length + 1 size, so there is a terminating zero at the end. + */ +String::String(unsigned length) { + this->length = length; + str = new char[length + 1]; + str[length] = '\0'; +} + +/*! \params str C-style string + */ +String::String(const char* str) { + length = strlen(str); + this->str = new char[length + 1]; // capture terminating zero + strncpy(this->str, str, length + 1); +} + +const char* String::get_cstr() const { + return str; +} + +unsigned String::get_length() const { + return length; +} + +char& String::operator[](unsigned index) { + return str[index]; +} + +/*! \params str C-style string + */ +String& String::operator+=(const char* str) { + unsigned strLen = strlen(str); + char* newStr = new char[length + strLen + 1]; + strncpy(newStr, this->str, length + 1); + strncat(newStr, str, strLen + 1); + + delete[] this->str; + this->str = newStr; + length += strLen; + + return *this; +} + +/*! + * Converts the number to a C-style string and then uses += to append it. + */ +String& String::operator+=(unsigned number) { + unsigned tempLen = 0; + for (unsigned tmp = number; tmp > 0 || tempLen == 0; tmp /= 10) + tempLen++; + + char* tmp = new char[tempLen + 1]; + tmp[tempLen] = '\0'; + for (unsigned i = tempLen - 1; i < tempLen; i--, number /= 10) + tmp[i] = number % 10 + '0'; + + *this += tmp; + delete[] tmp; + return *this; +} + +String::String() : String("") {} + +String::String(const String& other) { + copyFrom(other); +} + +String& String::operator=(const String& other) { + if (this != &other) { + free(); + copyFrom(other); + } + return *this; +} + +String::~String() { + free(); +} + +String::String(String&& other) { + str = other.str; + other.str = nullptr; + length = other.length; +} + +String& String::operator=(String&& other) { + if (this != &other) { + delete[] str; + str = other.str; + other.str = nullptr; + length = other.length; + } + return *this; +} + +/*! \param istr An input stream + * + * Directly reads bytes from stream (calls read() function). + * First reads the string length, then the underlying C-style string (including terminating zero). + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ifstream + */ +void String::read(std::istream& istr) { + istr.read((char*)&length, sizeof(length)); + delete[] str; + str = new char[length + 1]; + istr.read(str, sizeof(char) * (length + 1)); +} + +/*! \param ostr An output stream + * + * Directly writes bytes to stream (calls write() function). + * First writes the string length, then the underlying C-style string (including terminating zero). + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ofstream + */ +void String::write(std::ostream& ostr) const { + ostr.write((char*)&length, sizeof(length)); + ostr.write(str, sizeof(char) * (length + 1)); +} + +/*! \params other C-style string + * \returns strcmp between underlying C-style string and "other" + */ +int String::compare(const String& other) const { + return strcmp(str, other.str); +} + +/*! + * Uses the stream's getline function to read the data. + * + * \warning It takes at most 1024 characters from the stream! + * \note Best used with std::cin or text std::ifstream + */ +std::istream& operator>>(std::istream& istr, String& event) { + istr.getline(event.str, 1024); + event.length = strlen(event.str); + + return istr; +} + +/*! + * Uses the stream's << operator to write the underlying C-style string + * + * \note Best used with std::cout or text std::ofstream + */ +std::ostream& operator<<(std::ostream& ostr, const String& event) { + return ostr << event.str; +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Generic/String/String.h b/Year 1/Object-oriented Programming/Project 1/src/Generic/String/String.h new file mode 100644 index 0000000..84fbe9c --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Generic/String/String.h @@ -0,0 +1,56 @@ +#ifndef HEADER_STRING +#define HEADER_STRING + +/*! \file String.h + * \brief Stores declaration of class String + */ + +#include <istream> +#include <ostream> + +class String { + char* str; + unsigned length; + + void free(); + void copyFrom(const String& other); + +public: + //! The underlying array is allocated with length (+ 1) size + String(unsigned length); + //! Copies str + String(const char* str); + //! Returns internal C-style string + const char* get_cstr() const; + //! Returns number of characters in string + unsigned get_length() const; + //! Returns a character reference (from the underlying C-style string) at the given index + char& operator[](unsigned index); + + //! Appends a C-style string + String& operator+=(const char* str); + //! Appends a number + String& operator+=(unsigned number); + + String(); + String(const String& other); + String& operator=(const String& other); + ~String(); + + String(String&& other); + String& operator=(String&& other); + + //! Reads String from stream + void read(std::istream& istr); + //! Writes String to stream + void write(std::ostream& ostr) const; + //! Compares two strings + int compare(const String& other) const; + + //! Reads String from stream with >> operator + friend std::istream& operator>>(std::istream& istr, String& event); + //! Writes String to stream with << operator + friend std::ostream& operator<<(std::ostream& ostr, const String& event); +}; + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Models/Event.cpp b/Year 1/Object-oriented Programming/Project 1/src/Models/Event.cpp new file mode 100644 index 0000000..c4557b7 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Models/Event.cpp @@ -0,0 +1,153 @@ +#include "Event.h" +#include "Reservation.h" + +Event::Event() : hall(), name(), date() {} + +Event::Event(const Hall& hall, String name, Date date) : tickets(), reservations() { + this->hall = hall; + this->name = name; + this->date = date; +} + +OrderedList<Ticket>& Event::get_tickets() { + return tickets; +} + +const OrderedList<Ticket>& Event::get_tickets() const { + return tickets; +} + +OrderedList<Reservation>& Event::get_reservations() { + return reservations; +} + +const OrderedList<Reservation>& Event::get_reservations() const { + return reservations; +} + +const Hall& Event::get_hall() const { + return hall; +} + +const String& Event::get_name() const { + return name; +} + +const Date& Event::get_date() const { + return date; +} + +/*! \param istr An input stream + * + * Directly reads bytes from stream (calls read() function). + * Stored types must have defined a read() function. + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ifstream + */ +void Event::read(std::istream& istr) { + hall.read(istr); + name.read(istr); + date.read(istr); + tickets.read(istr); + reservations.read(istr); +} + +/*! \param ostr An output stream + * + * Directly writes bytes to stream (calls write() function). + * Stored types must have defined a read() function. + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ofstream + */ +void Event::write(std::ostream& ostr) { + hall.write(ostr); + name.write(ostr); + date.write(ostr); + tickets.write(ostr); + reservations.write(ostr); +} + +/*! \return compare of halls, or if they are equal, compare of dates + */ +int Event::compare(const Event& other) { + if (hall.compare(other.hall) == 0) + return date.compare(other.date); + return hall.compare(other.hall); +} + +/*! + * Uses the stream's >> operator to read and parse all internal values. + * Stored types must have defined operator >>. + * + * \note Best used with std::cin or text std::ifstream + */ +std::istream& operator>>(std::istream& istr, Event& event) { + Hall newHall; + String newString; + Date newDate; + OrderedList<Ticket> newTickets; + OrderedList<Reservation> newReservation; + + istr >> newHall >> newString >> newDate >> newTickets >> newReservation; + event = Event(newHall, newString, newDate); + event.get_tickets() = newTickets; + event.get_reservations() = newReservation; + + return istr; +} + +unsigned insertionOpSet = 0; + +/*! \param setting A number, defining the different options. It's best if it is passed as binary number. + * + * Sets a configuration setting for how operator<< should behave. Setting is stored as a global variable in the cpp file, so it's independent of Event instance. + * + * Each bit in the number is a certain setting. Each bit from left to right represents one of these options, from top to bottom: + * 1. Write count of bought tickets (available only if 5. is set) + * 2. Write count of reserved tickets (available only if 5. is set) + * 3. Write reservations + * 4. Write bought tickets + * 5. Write Hall information + * + * \par Example + * To configure printing the Hall information, bought tickets count and the bought tickets themselves, you would do: + * \code{.cpp} + * configureEventInsertionOp(0b10011); + * \endcode + */ +void configureEventInsertionOp(unsigned setting) { + insertionOpSet = setting; +} + +/*! + * Uses the stream's << operator to write all internal data, following setting from configureEventInsertionOp() + * Stored types must have defined operator <<. + * + * \note Best used with std::cout or text std::ofstream + */ +std::ostream& operator<<(std::ostream& ostr, const Event& event) { + if (insertionOpSet & 1) { + ostr << "| Hall: " << event.get_hall().get_number() << " | Name: " << event.get_name() << " | Date: " << event.get_date(); + + if (insertionOpSet & 8) + ostr << " || Tickets Count: " << event.get_tickets().get_count(); + if (insertionOpSet & 16) + ostr << " || Reservations Count: " << event.get_reservations().get_count(); + ostr << std::endl; + } + if (insertionOpSet & 2) { + ostr << "Bought tickets: "; + for (unsigned i = 0; i < event.get_tickets().get_count(); i++) + ostr << "[" << event.get_tickets()[i] << "] "; + ostr << std::endl; + } + if(insertionOpSet & 4) { + ostr << "Reserved tickets: "; + for (unsigned i = 0; i < event.get_reservations().get_count(); i++) + ostr << "[" << event.get_reservations()[i] << "] "; + ostr << std::endl; + } + return ostr; +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Models/Event.h b/Year 1/Object-oriented Programming/Project 1/src/Models/Event.h new file mode 100644 index 0000000..1162b8f --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Models/Event.h @@ -0,0 +1,63 @@ +#ifndef HEADER_MODEL_EVENT +#define HEADER_MODEL_EVENT + +/*! \file Event.h + * \brief Stores the declaration of class Event + */ + +#include "../Generic/List/OrderedList.hpp" +#include "../Generic/Date/Date.h" +#include "../Generic/String/String.h" +#include "Hall.h" +#include "Ticket.h" +#include "Reservation.h" +#include <istream> +#include <ostream> + +/*! \brief Stores an event's hall, name, date, bought tickets and reservations + */ +class Event { + Hall hall; + String name; + Date date; + OrderedList<Ticket> tickets; + OrderedList<Reservation> reservations; + +public: + //! Calls default constructors on data + Event(); + //! Copies given hall and sets name and date + Event(const Hall& hall, String name, Date date); + + //! Returns constant reference Hall in which event will be held + const Hall& get_hall() const; + //! Returns constant reference to name of event + const String& get_name() const; + //! Returns constant reference to date on which event will be held + const Date& get_date() const; + + //! Returns reference to bought tickets + OrderedList<Ticket>& get_tickets(); + //! Returns constant reference to bought tickets + const OrderedList<Ticket>& get_tickets() const; + //! Returns reference to reservations + OrderedList<Reservation>& get_reservations(); + //! Returns constant reference to reservations + const OrderedList<Reservation>& get_reservations() const; + + //! Reads Event from stream + void read(std::istream& istr); + //! Writes Event to stream + void write(std::ostream& ostr); + //! Compares two events + int compare(const Event& other); +}; + +//! Reads Event from stream with >> operator +std::istream& operator>>(std::istream& istr, Event& event); +//! Configures how Event should be shown, when written to stream with << operator +void configureEventInsertionOp(unsigned setting); +//! Writes Event to stream with << operator +std::ostream& operator<<(std::ostream& ostr, const Event& event); + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Models/Hall.cpp b/Year 1/Object-oriented Programming/Project 1/src/Models/Hall.cpp new file mode 100644 index 0000000..feacdac --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Models/Hall.cpp @@ -0,0 +1,76 @@ +#include "Hall.h" + +Hall::Hall() : Hall(0, 0, 0) {} + +Hall::Hall(int number) : Hall(number, 0, 0) {} + +Hall::Hall(int number, unsigned rows, unsigned seatsPerRow) { + this->number = number; + this->rows = rows; + this->seatsPerRow = seatsPerRow; +} + +int Hall::get_number() const { + return number; +} +unsigned Hall::get_rows() const { + return rows; +} + +unsigned Hall::get_seatsPerRow() const { + return seatsPerRow; +} + +/*! \param istr An input stream + * + * Directly reads bytes from stream (calls read() function). + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ifstream + */ +void Hall::read(std::istream& istr) { + istr.read((char*)&number, sizeof(number)); + istr.read((char*)&rows, sizeof(rows)); + istr.read((char*)&seatsPerRow, sizeof(seatsPerRow)); +} + +/*! \param ostr An output stream + * + * Directly writes bytes to stream (calls write() function). + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ofstream + */ +void Hall::write(std::ostream& ostr) const { + ostr.write((char*)&number, sizeof(number)); + ostr.write((char*)&rows, sizeof(rows)); + ostr.write((char*)&seatsPerRow, sizeof(seatsPerRow)); +} + +/*! \return -1 if number < other.number, 1 if number > other.number, 0 if number == other.number + */ +int Hall::compare(const Hall& other) const { + return (number > other.get_number()) - (number < other.get_number()); +} + +/*! + * Uses the stream's >> operator to read and parse all internal values. + * + * \note Best used with std::cin or text std::ifstream + */ +std::istream& operator>>(std::istream& istr, Hall& hall) { + int number; + unsigned rows, seatsPerRow; + istr >> number >> rows >> seatsPerRow; + hall = Hall(number, rows, seatsPerRow); + return istr; +} + +/*! + * Uses the stream's << operator to write all internal data. + * + * \note Best used with std::cout or text std::ofstream + */ +std::ostream& operator<<(std::ostream& ostr, const Hall& hall) { + return ostr << hall.get_number() << " " << hall.get_rows() << " "<< hall.get_seatsPerRow(); +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Models/Hall.h b/Year 1/Object-oriented Programming/Project 1/src/Models/Hall.h new file mode 100644 index 0000000..c567c75 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Models/Hall.h @@ -0,0 +1,47 @@ +#ifndef HEADER_MODEL_HALL +#define HEADER_MODEL_HALL + +/*! \file Hall.h + * \brief Stores the declaration of class Hall + */ + +#include <istream> +#include <ostream> + +/*! \brief Each hall contains a number, rows and seats per row + */ +class Hall { + int number; + unsigned rows; + unsigned seatsPerRow; + +public: + //! Sets values to 0 + Hall(); + //! Sets values, except number, to 0 + Hall(int number); + //! Sets values + Hall(int number, unsigned rows, unsigned seatsPerRow); + + //! Returns a Hall's number + int get_number() const; + //! Returns a Hall's row count + unsigned get_rows() const; + //! Return a Hall's seats per row count + unsigned get_seatsPerRow() const; + + //! Reads Hall from stream + void read(std::istream& istr); + //! Writes Hall to steam + void write(std::ostream& ostr) const; + //! Compare two Halls + int compare(const Hall& other) const; + +}; + +//! Reads Hall from stream with >> operator +std::istream& operator>>(std::istream& istr, Hall& hall); +//! Writes Hall to stream with << operator +std::ostream& operator<<(std::ostream& ostr, const Hall& hall); + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Models/Reservation.cpp b/Year 1/Object-oriented Programming/Project 1/src/Models/Reservation.cpp new file mode 100644 index 0000000..002ba29 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Models/Reservation.cpp @@ -0,0 +1,79 @@ +#include "Reservation.h" +#include <cstring> + +Reservation::Reservation() : Reservation(Ticket(), "", "") {} + +Reservation::Reservation(const Ticket& ticket) : Reservation(ticket, "", "") {} + +Reservation::Reservation(const Ticket& ticket, const char* password, const char* note) { + this->ticket = ticket; + strncpy(this->password, password, PASSWORD_LEN); + strncpy(this->note, note, NOTE_LEN); +} + +const Ticket& Reservation::get_ticket() const { + return ticket; +} + +const char* Reservation::get_password() const { + return password; +} + +const char* Reservation::get_note() const { + return note; +} + +/*! \param istr An input stream + * + * Directly reads bytes from stream (calls read() function). + * Uses ticket's read() function. + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ifstream + */ +void Reservation::read(std::istream& istr) { + ticket.read(istr); + istr.read(password, sizeof(password)); + istr.read(note, sizeof(note)); +} + +/*! \param ostr An output stream + * + * Directly writes bytes to stream (calls write() function). + * Uses ticket's write() function. + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ofstream + */ +void Reservation::write(std::ostream& ostr) const { + ticket.write(ostr); + ostr.write(password, sizeof(password)); + ostr.write(note, sizeof(note)); +} + +/*! \return Compares tickets + */ +int Reservation::compare(const Reservation& other) const { + return ticket.compare(other.ticket); +} + +/*! + * Uses the stream's >> operator to read and parse the ticket. + * Password and note are left blank. + * + * \note Best used with std::cin or text std::ifstream + */ +std::istream& operator>>(std::istream& istr, Reservation& reservation) { + strncpy(reservation.password, "", PASSWORD_LEN); + strncpy(reservation.note, "", NOTE_LEN); + return istr >> reservation.ticket; +} + +/*! + * Uses the stream's << operator to write the ticket only. + * + * \note Best used with std::cout or text std::ofstream + */ +std::ostream& operator<<(std::ostream& ostr, const Reservation& reservation) { + return ostr << reservation.ticket; +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Models/Reservation.h b/Year 1/Object-oriented Programming/Project 1/src/Models/Reservation.h new file mode 100644 index 0000000..10035b9 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Models/Reservation.h @@ -0,0 +1,50 @@ +#ifndef HEADER_MODEL_RESERVATION +#define HEADER_MODEL_RESERVATION + +/*! \file Reservation.h + * \brief Stores the declaration of class Reservation + */ + +#include "Ticket.h" +#include <istream> +#include <ostream> + +#define PASSWORD_LEN 8 +#define NOTE_LEN 32 + +/*! \brief Each Reservations contains a ticket, a password and a note + */ +class Reservation { + Ticket ticket; + char password[PASSWORD_LEN]; + char note[NOTE_LEN]; + +public: + //! Sets ticket as default value and leaves password and note blank + Reservation(); + //! Sets ticket and leaves password and note blank + Reservation(const Ticket& ticket); + //! Sets ticket, password and note + Reservation(const Ticket& ticket, const char* password, const char* note); + + //! Returns constant reference to ticket + const Ticket& get_ticket() const; + //! Returns constant C-style string to password + const char* get_password() const; + //! Returns constant C-style string to note + const char* get_note() const; + + //! Reads Reservation from stream + void read(std::istream& istr); + //! Writes Reservation to stream + void write(std::ostream& ostr) const; + //! Compares two Reservations + int compare(const Reservation& other) const; + + //! Reads Reservation from stream with >> operator + friend std::istream& operator>>(std::istream& istr, Reservation& reservation); + //! Writes Reservation to stream with << operator + friend std::ostream& operator<<(std::ostream& ostr, const Reservation& reservation); +}; + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Models/Ticket.cpp b/Year 1/Object-oriented Programming/Project 1/src/Models/Ticket.cpp new file mode 100644 index 0000000..bc8ef84 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Models/Ticket.cpp @@ -0,0 +1,71 @@ +#include "Ticket.h" + +Ticket::Ticket() : Ticket(0, 0) {} + +Ticket::Ticket(unsigned row, unsigned seat) { + this->row = row; + this->seat = seat; +} + +unsigned Ticket::get_row() const { + return row; +} + +unsigned Ticket::get_seat() const { + return seat; +} + +/*! \param istr An input stream + * + * Directly reads bytes from stream (calls read() function). + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ifstream + */ +void Ticket::read(std::istream& istr) { + istr.read((char*)&row, sizeof(row)); + istr.read((char*)&seat, sizeof(seat)); +} + +/*! \param ostr An output stream + * + * Directly writes bytes to stream (calls write() function). + * + * \remark Doesn't alter the stream in any other way. + * \note Best used with binary ofstream + */ +void Ticket::write(std::ostream& ostr) const { + ostr.write((const char*)&row, sizeof(row)); + ostr.write((const char*)&seat, sizeof(seat)); +} + +/*! \return If rows are equal, -1 if seat < other.seat, 1 if seat > other.seat, 0 if seat == other.seat, else the same, but for rows + */ +int Ticket::compare(const Ticket& other) const { + if (row == other.row) + return (seat > other.seat) - (seat < other.seat); + return (row > other.row) - (row < other.row); +} + +/* Outside of class */ + +/*! + * Uses the stream's >> operator to read and parse row and seat. + * + * \note Best used with std::cin or text std::ifstream + */ +std::istream& operator>>(std::istream& istr, Ticket& ticket) { + unsigned row, seat; + istr >> row >> seat; + ticket = Ticket(row, seat); + return istr; +} + +/*! + * Uses the stream's << operator to write row and seat, separated by a space. + * + * \note Best used with std::cout or text std::ofstream + */ +std::ostream& operator<<(std::ostream& ostr, const Ticket& ticket) { + return ostr << ticket.get_row() << " " << ticket.get_seat(); +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Models/Ticket.h b/Year 1/Object-oriented Programming/Project 1/src/Models/Ticket.h new file mode 100644 index 0000000..06b4571 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Models/Ticket.h @@ -0,0 +1,41 @@ +#ifndef HEADER_HEADER_TICKET +#define HEADER_HEADER_TICKET + +/*! \file Ticket.h + * \brief Stores the declaration of class Ticket + */ + +#include <istream> +#include <ostream> + +/*! \brief Each Ticket contains a row and a seat number + */ +class Ticket { + unsigned row; + unsigned seat; + +public: + //! Sets row and seat to zeroes + Ticket(); + //! Sets row and seat + Ticket(unsigned row, unsigned seat); + + //! Returns row + unsigned get_row() const; + //! Returns seat + unsigned get_seat() const; + + //! Reads Ticket from stram + void read(std::istream& istr); + //! Writes Ticket to stream + void write(std::ostream& ostr) const; + //! Compares two Tickets + int compare(const Ticket& other) const; +}; + +//! Reads Ticket from stream with >> operator +std::istream& operator>>(std::istream& istr, Ticket& ticket); +//! Writes Ticket to stream with << operator +std::ostream& operator<<(std::ostream& ostr, const Ticket& ticket); + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Services/EventService.cpp b/Year 1/Object-oriented Programming/Project 1/src/Services/EventService.cpp new file mode 100644 index 0000000..7ee757e --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Services/EventService.cpp @@ -0,0 +1,369 @@ +#include "EventService.h" +#include "StatusCode.h" +#include <cstring> +#include <fstream> + +/* Private */ + +unsigned EventService::indexOfEvent(const char* name, const Date& date) { + for (unsigned i = 0; i < events.get_count(); i++) { + if (events[i].get_date().compare(date) == 0 && events[i].get_name().compare(name) == 0) + return i; + } + return events.get_count(); +} + +bool correctReservationPassword(const Reservation& res, const char* password) { + return strcmp(res.get_password(), password) == 0; +} + +/* Public */ + +EventService::EventService(const HallService* hs) { + this->hs = hs; + events = OrderedList<Event>(); +} + +EventService::EventService(const HallService* hs, const Event* events, unsigned eventCount) { + this->hs = hs; + this->events = OrderedList<Event>(events, eventCount); +} + +/*! + * Uses HallService pointer to get Hall from hallNumber + * \return E_HallDoesntExist \b if hallNumber doesn't correspond to any Hall, saved in HallService + * \return E_EventWillOverlap \b if Event's date overlaps with another event in the same Hall + * \return Success \b otherwise + */ +StatusCode EventService::createEvent(int hallNumber, const String& name, const Date& date) { + unsigned hInd = hs->get_halls().findIndex(Hall(hallNumber)); + if (hInd == hs->get_halls().get_count()) + return E_HallDoesntExist; + + Event newEv(hs->get_halls()[hInd], name, date); + + unsigned eInd = events.findIndex(newEv); + if (eInd < events.get_count()) + return E_EventWillOverlap; + + events.insert(newEv); + return Success; +} + +/*! + * \return E_EventDoesNotExist \b if No Event with the given name and date exists + * \return Success \b otherwise + */ +StatusCode EventService::cancelEvent(const char* name, const Date& date) { + unsigned ind = indexOfEvent(name, date); + if (ind == events.get_count()) + return E_EventDoesNotExist; + + events.removeAt(ind); + return Success; +} + +/*! + * \return E_EventDoesNotExist \b if No Event with the given name and date exists + * \return E_TicketAlreadyBought \b if A ticket for the given row and seat has already been bought + * \return E_TicketAlreadyReserved \b if A reservation has already been placed on the given row and seat + * \return Success \b otherwise + */ +StatusCode EventService::reserveTicket(const char* name, const Date& date, const char* password, const char* note, const Ticket& ticket) { + unsigned eInd = indexOfEvent(name, date); + if (eInd == events.get_count()) + return E_EventDoesNotExist; + + if (events[eInd].get_hall().get_rows() < ticket.get_row() || events[eInd].get_hall().get_seatsPerRow() < ticket.get_seat()) + return E_InvalidSeat; + + unsigned tInd = events[eInd].get_tickets().findIndex(ticket); + if (tInd < events[eInd].get_tickets().get_count()) + return E_TicketAlreadyBought; + + Reservation res(ticket, password, note); + + unsigned rInd = events[eInd].get_reservations().findIndex(res); + if (rInd < events[eInd].get_reservations().get_count()) + return E_TicketAlreadyReserved; + + events[eInd].get_reservations().insert(res); + return Success; +} + +/*! + * Canceling a reservation doesn't require password and doesn't return the Reservation's note + * + * \return E_EventDoesNotExist \b if No Event with the given name and date exists + * \return W_TicketHadNotBeenReserved \b Ticket hadn't been reserved in the first place + * \return Success \b otherwise + */ +StatusCode EventService::cancelTicketReservation(const char* name, const Date& date, const Ticket& ticket) { + unsigned eInd = indexOfEvent(name, date); + if (eInd == events.get_count()) + return E_EventDoesNotExist; + + if (events[eInd].get_hall().get_rows() < ticket.get_row() || events[eInd].get_hall().get_seatsPerRow() < ticket.get_seat()) + return E_InvalidSeat; + + unsigned rInd = events[eInd].get_reservations().findIndex(ticket); + if (rInd == events[eInd].get_reservations().get_count()) + return W_TicketHadNotBeenReserved; + + events[eInd].get_reservations().removeAt(rInd); + return Success; +} + +/*! + * \return E_EventDoesNotExist \b if No Event with the given name and date exists + * \return W_TicketHadNotBeenReserved \b Ticket hadn't been reserved in the first place + * \return Success \b otherwise + */ +StatusCode EventService::ticketIsReserved(const char* name, const Date& date, const Ticket& ticket) { + unsigned eInd = indexOfEvent(name, date); + if (eInd == events.get_count()) + return E_EventDoesNotExist; + + if (events[eInd].get_reservations().contain(ticket)) + return Success; + else + return W_TicketHadNotBeenReserved; +} + +/*! + * Calls buyTicket with Reservation, but sets password and noteOutput to nullptr. + * \copydetails buyTicket(const char* name, const Date& date, const Ticket& ticket, const char* password, String* reservationNoteOutput) + */ +StatusCode EventService::buyTicket(const char* name, const Date& date, const Ticket& ticket) { + return buyTicket(name, date, ticket, nullptr, nullptr); +} + +/*! + * \return E_EventDoesNotExist \b if No Event with the given name and date exists + * \return E_TicketAlreadyBought \b if A ticket for the given row and seat has already been bought + * \return E_TicketAlreadyReserved \b if The seat and row are reserved, but given password is nullptr + * \return E_WrongReservationPassword \b if The seat and row are reserved, but given password is incorrect + * \return Success \b otherwise + */ +StatusCode EventService::buyTicket(const char* name, const Date& date, const Ticket& ticket, const char* password, String* reservationNoteOutput) { + unsigned eInd = indexOfEvent(name, date); + if (eInd == events.get_count()) + return E_EventDoesNotExist; + + if (events[eInd].get_hall().get_rows() < ticket.get_row() || events[eInd].get_hall().get_seatsPerRow() < ticket.get_seat()) + return E_InvalidSeat; + + unsigned tInd = events[eInd].get_tickets().findIndex(ticket); + if (tInd < events[eInd].get_tickets().get_count()) + return E_TicketAlreadyBought; + + unsigned rInd = events[eInd].get_reservations().findIndex(ticket); + if (rInd < events[eInd].get_reservations().get_count()) { + if (password == nullptr) + return E_TicketAlreadyReserved; + if (!correctReservationPassword(events[eInd].get_reservations()[rInd], password)) + return E_WrongReservationPassword; + + if (reservationNoteOutput != nullptr) + *reservationNoteOutput = events[eInd].get_reservations()[rInd].get_note(); + events[eInd].get_reservations().removeAt(rInd); + } + + events[eInd].get_tickets().insert(ticket); + return Success; +} + +/*! + * Each seat in the Hall of the Event is represented by a single character, where 'R' means the seat is reserved, 'B' means the seat is bought and ' ' if it's neither. + * + * \return Empty String \b if No Event with the given name and date exists + * \return Proper String \b otherwise + */ +String EventService::createSeatingString(const char* name, const Date& date, unsigned* seatsPerRow) { + unsigned eInd = indexOfEvent(name, date); + if (eInd == events.get_count()) { + return ""; + } + + unsigned rowCount = events[eInd].get_hall().get_rows(), seatsCount = events[eInd].get_hall().get_seatsPerRow(); + *seatsPerRow = seatsCount; + + String out(rowCount * seatsCount); + + for (unsigned acc = 0, row = 1; row <= rowCount; row++) { + for (unsigned seat = 1; seat <= seatsCount; seat++, acc++) { + if (events[eInd].get_tickets().contain(Ticket(row, seat))) + out[acc] = 'B'; + else if (events[eInd].get_reservations().contain(Ticket(row, seat))) + out[acc] = 'R'; + else + out[acc] = ' '; + } + } + + return out; +} + +unsigned indexToInsertWhenTicketSort(const List<Event>& elem, unsigned topN, unsigned ticketCount) { + for (unsigned i = 0; i < elem.get_count() && i < topN; i++) { + if (elem[i].get_tickets().get_count() <= ticketCount) + return i; + } + return (topN < elem.get_count()) ? topN : elem.get_count(); +} + +/*! + * Events are sorted from index 0 to last index decreasingly by number of watchers. + * Watchers count for an Event is the amount of bought tickets. + * + * If there are less events than topN, all events are returned. + */ +List<Event> EventService::queryMostWatched(unsigned topN) { + List<Event> result; + for (unsigned i = 0; i < events.get_count(); i++) { + unsigned ind = indexToInsertWhenTicketSort(result, topN, events[i].get_tickets().get_count()); + if (ind < topN) + result.insertAt(events[i], ind); + if (result.get_count() > topN) + result.removeAt(topN); + } + return result; +} + +List<Event> EventService::queryInsufficientlyVisited() { + List<Event> result; + for (unsigned i = 0; i < events.get_count(); i++) { + if (events[i].get_tickets().get_count() < events[i].get_hall().get_rows() * events[i].get_hall().get_seatsPerRow() / 10) + result.add(events[i]); + } + return result; +} + +template <typename T> +bool cond_event_all(const Event& e1, T scrap) { + return true; +} + +bool cond_event_name(const Event& e, const char* name) { + return e.get_name().compare(name) == 0; +} + +bool cond_event_date(const Event& e, const Date& date) { + return e.get_date().compare(date) == 0; +} + +/*! \param name C-style string, if it is equal to "ALL", reservations from all Events on the given date will be reported + * \param date If day, month and year are equal to 0, reservations from all dates will be reported + * + * Report is saved in a file in the current directory, named "report-NAME-DATE.txt", + * where NAME is the given name, and DATE is the given date or "ALL" if the date is 0/0/0 + * + * All Events are inserted into the file with the << operator and a setting of 0b00101. + * + * \return E_FileCouldNotBeOpened \b if Report file couldn't be created + * \return Success \b otherwise + */ +StatusCode EventService::reportReservations(const char* name, const Date& date) { + List<Event> result; + bool (*cond_name)(const Event&, const char*) = cond_event_name; + bool (*cond_date)(const Event&, const Date&) = cond_event_date; + + if (strcmp(name, "ALL") == 0) cond_name = cond_event_all; + if (date.compare(Date(0, 0, 0)) == 0) cond_date = cond_event_all; + + for (unsigned i = 0; i < events.get_count(); i++) { + if (cond_name(events[i], name) && cond_date(events[i], date)) + result.add(events[i]); + } + + String outFileName("report-"); + (outFileName += name) += "-"; + if (date.compare(Date(0, 0, 0)) == 0) + outFileName += "ALL"; + else + outFileName += date.createString().get_cstr(); + outFileName += ".txt"; + + std::ofstream outFile(outFileName.get_cstr()); + if (!outFile.is_open()) + return E_FileCouldNotBeOpened; + + configureEventInsertionOp(0b00101); + outFile << result; + outFile.close(); + + return Success; +} + +bool cond_event_hallNumber(const Event& event, int hallNumber) { + return event.get_hall().get_number() == hallNumber; +} + +bool cond_event_dates(const Event& event, const Date& start, const Date& end) { + return event.get_date().compare(start) >= 0 && event.get_date().compare(end) <= 0; +} + +/*! \param hallNumber Number of the Event's Hall + * \param start Starting Date from which to take Events + * \param end Ending Date until which to take Evnets (including) + * + * Report is saved in a file named "boughtTickets.txt" in the current directory. + * + * All Events are inserted into the file with the << operator and a setting of 0b01001. + * + * \return E_FileCouldNotBeOpened \b if Report file couldn't be created + * \return Success \b otherwise + */ +StatusCode EventService::reportBoughtTickets(int hallNumber, const Date& start, const Date& end, bool all) { + List<Event> result; + bool (*cond_hall)(const Event&, int) = cond_event_hallNumber; + if (all) cond_hall = cond_event_all; + + for (unsigned i = 0; i < events.get_count(); i++) { + if (cond_hall(events[i], hallNumber) && cond_event_dates(events[i], start, end)) + result.add(events[i]); + } + + std::ofstream outFile("boughtTickets.txt"); + if (!outFile.is_open()) + return E_FileCouldNotBeOpened; + + configureEventInsertionOp(0b01001); + outFile << result; + outFile.close(); + + return Success; + +} + +/*! + * Looks for file named "eventsDatabse.fmits", which is just a binary file, and then reads it with the "read" function in events List. + * + * \return E_FileCouldNotBeOpened \b if The files couldn't be opened/Doesn't exist + * \return Success \b otherwise + */ +StatusCode EventService::load() { + std::ifstream inFile("eventsDatabase.fmits"); + if (!inFile.is_open()) + return E_FileCouldNotBeOpened; + + events.read(inFile); + inFile.close(); + return Success; +} + +/*! + * Looks for file named "eventsDatabse.fmits", which is just a binary file, and then writes to it with the "write" function in events List. + * + * \return E_FileCouldNotBeOpened \b if The files couldn't be opened + * \return Success \b otherwise + */ +StatusCode EventService::save() { + std::ofstream outFile("eventsDatabase.fmits"); + if (!outFile.is_open()) + return E_FileCouldNotBeOpened; + + events.write(outFile); + outFile.close(); + return Success; +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Services/EventService.h b/Year 1/Object-oriented Programming/Project 1/src/Services/EventService.h new file mode 100644 index 0000000..f6c94ca --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Services/EventService.h @@ -0,0 +1,65 @@ +#ifndef HEADER_SERVICES_EVENTSERVICE +#define HEADER_SERVICES_EVENTSERVICE + +/*! \file EventService.h + * \brief Stores the declaration of class EventService + */ + +#include "../Generic/List/OrderedList.hpp" +#include "../Generic/Date/Date.h" +#include "../Models/Event.h" +#include "HallService.h" +#include "StatusCode.h" + +/*! \brief Each EventService stores all events and a pointer to HallService + */ +class EventService { + OrderedList<Event> events; + + const HallService* hs; + + unsigned indexOfEvent(const char* name, const Date& date); + +public: + //! Sets HallService pointer and initializes events List + EventService(const HallService* hs); + //! Sets HallService pointer and copies values from events into events List + EventService(const HallService* hs, const Event* events, unsigned eventCount); + + //! Creates an Event, with the given hallNumber, name and date + StatusCode createEvent(int hallNumber, const String& name, const Date& date); + //! Cancels an Event with the given name and date + StatusCode cancelEvent(const char* name, const Date& date); + + //! Creates a Reservation for an Event by name and date + StatusCode reserveTicket(const char* name, const Date& date, const char* password, const char* note, const Ticket& ticket); + //! Cancels a Reservation for a ticket + StatusCode cancelTicketReservation(const char* name, const Date& date, const Ticket& ticket); + //! Whether a ticket is a reserved or not + StatusCode ticketIsReserved(const char* name, const Date& date, const Ticket& ticket); + + //! Buys ticket, assuming it is reserved and stores the reservation note + StatusCode buyTicket(const char* name, const Date& date, const Ticket& ticket, const char* password, String* reservationNoteOutput); + //! Buys ticket, assuming it isn't reserved + StatusCode buyTicket(const char* name, const Date& date, const Ticket& ticket); + + //! Creates a special String for status of all seats in given Event (by name and date) and returns seatsPerRow in given pointer + String createSeatingString(const char* name, const Date& date, unsigned* seatsPerRow); + + //! \return A List of topN most watched Events + List<Event> queryMostWatched(unsigned topN); + //! \return A List of events which have attracted sales of less than 10% of the Hall's seat + List<Event> queryInsufficientlyVisited(); + + //! Creates a report that lists all Events (with given name and date) and their reserved tickets + StatusCode reportReservations(const char* name, const Date& date); + //! Creates a report that Lists all Events (with given name and date) and number of bought tickets + StatusCode reportBoughtTickets(int hallNumber, const Date& start, const Date& end, bool all = false); + + //! Loads Events from a database file + StatusCode load(); + //! Saves Events to a database file + StatusCode save(); +}; + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Services/HallService.cpp b/Year 1/Object-oriented Programming/Project 1/src/Services/HallService.cpp new file mode 100644 index 0000000..20c7a0f --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Services/HallService.cpp @@ -0,0 +1,42 @@ +#include "HallService.h" +#include <fstream> + +HallService::HallService(const Hall* halls, unsigned hallCount) { + this->halls = OrderedList<Hall>(halls, hallCount); +} + +const OrderedList<Hall>& HallService::get_halls() const { + return halls; +} + +/*! + * Looks for file named "hallsDatabse.fmits", which is just a binary file, and then reads it with the "read" function in events List. + * + * \return E_FileCouldNotBeOpened \b if The files couldn't be opened/Doesn't exist + * \return Success \b otherwise + */ +StatusCode HallService::load() { + std::ifstream inFile("hallsDatabase.fmits"); + if (!inFile.is_open()) + return E_FileCouldNotBeOpened; + + halls.read(inFile); + inFile.close(); + return Success; +} + +/*! + * Looks for file named "hallsDatabse.fmits", which is just a binary file, and then writes to it with the "write" function in events List. + * + * \return E_FileCouldNotBeOpened \b if The files couldn't be opened + * \return Success \b otherwise + */ +StatusCode HallService::save() { + std::ofstream outFile("hallsDatabase.fmits"); + if (!outFile.is_open()) + return E_FileCouldNotBeOpened; + + halls.write(outFile); + outFile.close(); + return Success; +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/Services/HallService.h b/Year 1/Object-oriented Programming/Project 1/src/Services/HallService.h new file mode 100644 index 0000000..09bda89 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Services/HallService.h @@ -0,0 +1,30 @@ +#ifndef HEADER_SERVICES_HALLSERVICE +#define HEADER_SERVICES_HALLSERVICE + +/*! \file HallService.h + * \brief Stores the declaration of class HallService + */ + +#include "../Generic/List/OrderedList.hpp" +#include "../Models/Hall.h" +#include "StatusCode.h" + +/*! \brief Each HallService stores all halls + */ +class HallService { + OrderedList<Hall> halls; + +public: + //! Copies halls from parameters + HallService(const Hall* halls, unsigned hallCount); + + //! Returns a constant reference to halls + const OrderedList<Hall>& get_halls() const; + + //! Loads Halls to a database file + StatusCode load(); + //! Saves Halls to a database file + StatusCode save(); +}; + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/Services/StatusCode.h b/Year 1/Object-oriented Programming/Project 1/src/Services/StatusCode.h new file mode 100644 index 0000000..9d3b94f --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/Services/StatusCode.h @@ -0,0 +1,35 @@ +#ifndef HEADER_SERVICES_STATUSCODE +#define HEADER_SERVICES_STATUSCODE + +/*! \file StatusCode.h + * \brief Stores declaration of enum StatusCode + */ + +/*! \brief Used for communication between Services and what uses them + * + * Most functions in Services return a StatusCode. + * + * Those starting with W should be considered Warning, and those starting with E should be considered Errors. + */ +enum StatusCode { + Success, + + + W_TicketHadNotBeenReserved, + + + E_FileCouldNotBeOpened, + + E_HallDoesntExist, + + E_EventWillOverlap, + E_EventDoesNotExist, + + E_InvalidSeat, + E_TicketAlreadyBought, + E_TicketAlreadyReserved, + E_ReservationDoesNotExist, + E_WrongReservationPassword, +}; + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/UserInterface/FMITicketSystemConsoleUI.cpp b/Year 1/Object-oriented Programming/Project 1/src/UserInterface/FMITicketSystemConsoleUI.cpp new file mode 100644 index 0000000..6e4321d --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/UserInterface/FMITicketSystemConsoleUI.cpp @@ -0,0 +1,295 @@ +#include "FMITicketSystemConsoleUI.h" +#include "../Generic/List/OrderedList.hpp" +#include "../Generic/String/String.h" +#include "../Generic/Date/Date.h" +#include "../Generic/ConsoleInterface/Menu.h" +#include "../Generic/ConsoleInterface/Toolbox.hpp" +#include "../Services/StatusCode.h" +#include "../Models/Reservation.h" +#include <cstring> + +EventService* es; +HallService* hs; + +Menu mainMenu("FMI Ticket System", true, false), + ticketManagementMenu("Ticket management", false, true), + eventManagementMenu("Event management", false, true), + reportsMenu("Report management", false, true); + +void handleStatusCode(StatusCode sc, Menu menu) { + switch (sc) { + case Success: + menu.registerSuccess("Success!"); break; + case W_TicketHadNotBeenReserved: + menu.registerWarning("Ticket hadn't been reserved. Nothing changed."); break; + case E_EventDoesNotExist: + menu.registerError("Event doesn't exist!"); break; + case E_EventWillOverlap: + menu.registerError("There is already an event at same time!"); break; + case E_TicketAlreadyBought: + menu.registerError("Ticket was already bought!"); break; + case E_TicketAlreadyReserved: + menu.registerError("Ticket was already reserved!"); break; + case E_ReservationDoesNotExist: + menu.registerError("Reservation doesn't exist!"); break; + case E_WrongReservationPassword: + menu.registerError("Wrong reservation password!"); break; + case E_FileCouldNotBeOpened: + menu.registerError("An error occurred while trying to open a file!"); break; + case E_InvalidSeat: + menu.registerError("The seat doesn't exist!"); break; + default: + menu.registerError("An unhandled status code was returned!"); break; + } +} + +/* Ticket Management */ + +void command_freeSeats() { + char name[MAX_LINE_WIDTH]; + inputLineSubBox("Enter event name: ", name, MAX_LINE_WIDTH); + Date date; + inputSubBox("Enter event date: ", &date); + + unsigned seatsPerRow = 0; + String seating = es->createSeatingString(name, date, &seatsPerRow); + if (seating.get_length() == 0) { + handleStatusCode(E_EventDoesNotExist, ticketManagementMenu); + return; + } + + table(1, seatsPerRow, seating.get_cstr()); + pressEnterToContinue(); +} + +void command_reserveTicket() { + char name[MAX_LINE_WIDTH]; + inputLineSubBox("Enter event name: ", name, MAX_LINE_WIDTH); + Date date; + inputSubBox("Enter event date [day month year]: ", &date); + Ticket tic; + inputSubBox("Enter row and seat [row seat]: ", &tic); + char password[PASSWORD_LEN]; + inputLineSubBox("Enter password [Max 8 characters]: ", password, PASSWORD_LEN); + char note[NOTE_LEN]; + inputLineSubBox("Enter note [Max 32 characters]: ", note, NOTE_LEN, false); + + StatusCode s = es->reserveTicket(name, date, password, note, tic); + handleStatusCode(s, ticketManagementMenu); +} + +void command_cancelReservation() { + char name[MAX_LINE_WIDTH]; + inputLineSubBox("Enter event name: ", name, MAX_LINE_WIDTH); + Date date; + inputSubBox("Enter event date [day month year]: ", &date); + Ticket tic; + inputSubBox("Enter row and seat [row seat]: ", &tic); + + StatusCode s = es->cancelTicketReservation(name, date, tic); + handleStatusCode(s, ticketManagementMenu); +} + +void command_buyTicket() { + char name[MAX_LINE_WIDTH]; + inputLineSubBox("Enter event name: ", name, MAX_LINE_WIDTH); + Date date; + inputSubBox("Enter event date [day month year]: ", &date); + Ticket tic; + inputSubBox("Enter row and seat [row seat]: ", &tic); + + StatusCode s = es->ticketIsReserved(name, date, tic); + if (s == Success) { + warningSubBox("Ticket is reserved!"); + + char pass[PASSWORD_LEN]; + inputLineSubBox("Enter reservation password: ", pass, PASSWORD_LEN); + + String note; + s = es->buyTicket(name, date, tic, pass, ¬e); + if (note.get_length() > 0) { + successSubBox("The reservation had a note attached. It reads:"); + successSubBox(note.get_cstr()); + pressEnterToContinue(false); + } + } + else if (s == W_TicketHadNotBeenReserved) { + s = es->buyTicket(name, date, tic); + } + + handleStatusCode(s, ticketManagementMenu); +} + +void submenu_ticketManagement() { + ticketManagementMenu.navigate(); +} + +/* Event Management */ + +void command_createEvent() { + int hallNumber; + inputSubBox("Enter hall number: ", &hallNumber); + char name[MAX_LINE_WIDTH]; + inputLineSubBox("Enter event name: ", name, MAX_LINE_WIDTH); + Date date; + inputSubBox("Enter event date [day month year]: ", &date); + + StatusCode s = es->createEvent(hallNumber, name, date); + handleStatusCode(s, eventManagementMenu); +} + +void command_cancelEvent() { + char name[MAX_LINE_WIDTH]; + inputLineSubBox("Enter event name: ", name, MAX_LINE_WIDTH); + Date date; + inputSubBox("Enter event date [day month year]: ", &date); + + StatusCode s = es->cancelEvent(name, date); + handleStatusCode(s, eventManagementMenu); +} + +void customPrintEvent(const Event& ev) { + print(ev.get_name()); + print(" || Hall "); + print(ev.get_hall().get_number()); + print(" @ "); + printLine(ev.get_date()); +} + +void command_mostWatched() { + unsigned topN; + inputSubBox("How many to take?: ", &topN); + + List<Event> top = es->queryMostWatched(topN); + + printLine(""); + print("The top "); + print(topN); + printLine(" most watched events are:"); + + resetOrderedList(1); + for (unsigned i = 0; i < top.get_count(); i++) { + _printOrderedListBeginning(); + customPrintEvent(top[i]); + } + + pressEnterToContinue(); +} + +void command_belowWatchers() { + printLine("Events with less than 10% bought tickets in hall:"); + + List<Event> least = es->queryInsufficientlyVisited(); + for (unsigned i = 0; i < least.get_count(); i++) { + customPrintEvent(least[i]); + } + + bool toDelete; + inputBox("Do you want to cancel them? [0/1]: ", &toDelete); + if (toDelete) { + StatusCode s = Success; + for (unsigned i = 0; i < least.get_count(); i++) + s = es->cancelEvent(least[i].get_name().get_cstr(), least[i].get_date()); + handleStatusCode(s, eventManagementMenu); + } +} + +void submenu_eventManagement() { + eventManagementMenu.navigate(); +} + +/* Report Management */ + +void command_reservationsList() { + char name[MAX_LINE_WIDTH]; + inputLineSubBox("Enter event name: ", name, MAX_LINE_WIDTH); + char dateStr[MAX_LINE_WIDTH]; + inputLineSubBox("Enter event date [day month year]: ", dateStr, MAX_LINE_WIDTH, false); + + Date date(dateStr); + + StatusCode s = es->reportReservations(name, date); + handleStatusCode(s, reportsMenu); +} + +void command_boughtTickets() { + char hallNumber[MAX_LINE_WIDTH]; + inputLineSubBox("Enter hall number: ", hallNumber, MAX_LINE_WIDTH); + Date start, end; + inputSubBox("Enter start date [day month year]: ", &start); + inputSubBox("Enter end date [day month year]: ", &end); + + StatusCode s; + if (strcmp(hallNumber, "ALL") == 0) + s = es->reportBoughtTickets(0, start, end, true); + else + s = es->reportBoughtTickets(atoi(hallNumber), start, end); + + handleStatusCode(s, reportsMenu); +} + +void submenu_reportManagement() { + reportsMenu.navigate(); +} + +/* Main */ + +void load() { + StatusCode s = es->load(); + if (s == E_FileCouldNotBeOpened) { + errorBox("Could not load events from file!"); + pressEnterToContinue(false); + } + s = hs->load(); + if (s == E_FileCouldNotBeOpened) { + errorBox("Could not load halls from file!"); + pressEnterToContinue(false); + } +} + +void save() { + StatusCode s = es->save(); + if (s == E_FileCouldNotBeOpened) { + errorBox("Could not save events to file!"); + pressEnterToContinue(); + } + s = hs->save(); + if (s == E_FileCouldNotBeOpened) { + errorBox("Could not save halls to file!"); + pressEnterToContinue(false); + } +} + +void init() { + mainMenu.addCommand(Command("Ticket Management", submenu_ticketManagement)); + mainMenu.addCommand(Command("Event Management", submenu_eventManagement)); + mainMenu.addCommand(Command("Report Management", submenu_reportManagement)); + + ticketManagementMenu.addCommand(Command("Print Seats", command_freeSeats)); + ticketManagementMenu.addCommand(Command("Reserve Ticket", command_reserveTicket)); + ticketManagementMenu.addCommand(Command("Cancel Ticket Reservation", command_cancelReservation)); + ticketManagementMenu.addCommand(Command("Buy Ticket", command_buyTicket)); + + eventManagementMenu.addCommand(Command("Create Event", command_createEvent)); + eventManagementMenu.addCommand(Command("Cancel Event", command_cancelEvent)); + eventManagementMenu.addCommand(Command("Query - Most Watched", command_mostWatched)); + eventManagementMenu.addCommand(Command("Query - Below 10% watchers", command_belowWatchers)); + + reportsMenu.addCommand(Command("Reservations List", command_reservationsList)); + reportsMenu.addCommand(Command("Bought Tickets", command_boughtTickets)); +} + +/*! + * Stores EventService and HallService pointers in internal global variables, + * calls EventService and HallService load() functions, + * initializes all menus, navigates the main menu and + * finally calls EventService and HallService save() functions + */ +void runUI(EventService* eventService, HallService* hallService) { + es = eventService; + hs = hallService; + load(); + init(); + mainMenu.navigate(); + save(); +} diff --git a/Year 1/Object-oriented Programming/Project 1/src/UserInterface/FMITicketSystemConsoleUI.h b/Year 1/Object-oriented Programming/Project 1/src/UserInterface/FMITicketSystemConsoleUI.h new file mode 100644 index 0000000..e8c4ac7 --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/UserInterface/FMITicketSystemConsoleUI.h @@ -0,0 +1,14 @@ +#ifndef HEADER_FMITICKETSYSTEMCONSOLEUI +#define HEADER_FMITICKETSYSTEMCONSOLEUI + +/*! \file FMITicketSystemConsoleUI.h + * \brief Stores declaration of runUI function + */ + +#include "../Services/EventService.h" +#include "../Services/HallService.h" + +//! Runs the console interface for the ticket system +void runUI(EventService* eventService, HallService* hallService); + +#endif diff --git a/Year 1/Object-oriented Programming/Project 1/src/main.cpp b/Year 1/Object-oriented Programming/Project 1/src/main.cpp new file mode 100644 index 0000000..bdf3a2b --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 1/src/main.cpp @@ -0,0 +1,23 @@ +/*! \file main.cpp + * \brief Entry point for application + * + * Contains the main() function. + */ + +#include "Services/EventService.h" +#include "Services/HallService.h" +#include "Models/Hall.h" +#include "UserInterface/FMITicketSystemConsoleUI.h" + +/*! + * Creates instances of HallService and EventService and + * executes runUI() function. + */ +int main() { + Hall myVenueHalls[] = { Hall(1, 20, 30), Hall(2, 15, 15), Hall(3, 10, 20) }; + + HallService hs(myVenueHalls, 3); + EventService es(&hs); + + runUI(&es, &hs); +} |
