From f20e49a71a7ee186057f5b79aeb58f9f22b352f4 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 9 Oct 2024 21:04:57 +0200 Subject: parse artists.csv --- Artist.h | 3 ++- CMakeLists.txt | 7 +++++ CSVParser.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CSVParser.h | 12 +++++++++ Canvas.cpp | 14 ++++++++++ Canvas.h | 12 ++++++--- Deserializer.cpp | 32 ++++++++++++++++++++++ Deserializer.h | 17 +++++++----- LocalFile.cpp | 34 ++++++++++++++--------- LocalFile.h | 3 ++- Museum.h | 2 +- Parser.cpp | 32 ++++++++++++++++++++++ Parser.h | 8 +++++- ParserStrategy.h | 4 --- People.h | 2 +- TXTParser.cpp | 16 +++++++++++ TXTParser.h | 12 +++++++++ Tile.cpp | 6 +++++ Tile.h | 4 +++ TileAppearance.h | 7 +++++ TileBehavior.h | 4 +++ XMLParser.cpp | 29 ++++++++++++++++++++ XMLParser.h | 12 +++++++++ main.cpp | 26 +++++++++++++++--- 24 files changed, 345 insertions(+), 35 deletions(-) create mode 100644 CSVParser.cpp create mode 100644 Canvas.cpp create mode 100644 Deserializer.cpp create mode 100644 Parser.cpp create mode 100644 TXTParser.cpp create mode 100644 Tile.cpp create mode 100644 XMLParser.cpp diff --git a/Artist.h b/Artist.h index c14785b..8f5ba2e 100644 --- a/Artist.h +++ b/Artist.h @@ -6,7 +6,8 @@ class Artist { public: void update(); -private: +public: ArtistData data; + }; diff --git a/CMakeLists.txt b/CMakeLists.txt index df8908f..6e47625 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,13 @@ add_executable(main LocalFile.cpp Exception.cpp FileReader.cpp + Canvas.cpp + Parser.cpp + CSVParser.cpp + XMLParser.cpp + # TXTParser.cpp + Deserializer.cpp + Tile.cpp ) target_link_libraries(main diff --git a/CSVParser.cpp b/CSVParser.cpp new file mode 100644 index 0000000..3fbe804 --- /dev/null +++ b/CSVParser.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include + +#include "CSVParser.h" +#include "Exception.h" +#include "Parser.h" + +using namespace std; + +CSVParser CSVParser::instance {}; +CSVParser::CSVParser() { + Parser::register_strategy(this); +} + +unsigned int CSVParser::heuristic(File & f) { + const string content = f.read(); + int global_columns = 0; + int columns = 1; + int rows = 0; + int penalty = 1; + for (char c : content) { + if (c == ',') columns++; + if (c == '\n') { + rows++; + if (global_columns == 0) global_columns = columns; + penalty += abs(global_columns - columns); + columns = 1; + } + } + if (global_columns == 1) penalty += 1000; + return (rows + global_columns) / penalty; +} + +static size_t header_idx(vector header, string field) { + auto iter = find(header.begin(), header.end(), field); + if (iter == header.end()) + throw Exception("CSV file is missing \"%s\" column", field.c_str()); + return iter - header.begin(); +} + +void CSVParser::parse(File & f, Deserializer & d) { + vector> table = {}; + + istringstream rows(f.read()); + string row; + while (getline(rows, row)) { + // ignore windows line endings + if (row.back() == '\r') row.pop_back(); + istringstream columns(row); + string column; + + vector table_row = {}; + while (getline(columns, column, ',')) { + table_row.push_back(column); + } + table.push_back(table_row); + } + + if (table.size() < 1) + throw Exception("not enough data rows in CSV file"); + + vector table_header = table[0]; + table.erase(table.begin()); + + size_t x_idx = header_idx(table_header, "x"); + size_t y_idx = header_idx(table_header, "y"); + size_t vx_idx = header_idx(table_header, "vx"); + size_t vy_idx = header_idx(table_header, "vy"); + + for (vector row : table) { + d.add_artist({ + .x = stof(row[x_idx]), + .y = stof(row[y_idx]), + .vx = stof(row[vx_idx]), + .vy = stof(row[vy_idx]), + }); + } +} + diff --git a/CSVParser.h b/CSVParser.h index 3f59c93..aa091a7 100644 --- a/CSVParser.h +++ b/CSVParser.h @@ -1,2 +1,14 @@ #pragma once +#include "ParserStrategy.h" + +class CSVParser : public ParserStrategy { +public: + virtual void parse(File & f, Deserializer & d); + virtual unsigned int heuristic(File & f); + +private: + static CSVParser instance; + CSVParser(); +}; + diff --git a/Canvas.cpp b/Canvas.cpp new file mode 100644 index 0000000..63dbe74 --- /dev/null +++ b/Canvas.cpp @@ -0,0 +1,14 @@ +#include "Canvas.h" + +Tile & Canvas::get_tile(unsigned x, unsigned y) { + return this->tiles[this->pos_to_index(x, y)]; +} + +void Canvas::set_tile(unsigned x, unsigned y, TileData t) { + this->tiles[this->pos_to_index(x, y)] = Tile(t); +} + +size_t Canvas::pos_to_index(unsigned x, unsigned y) { + return y * this->data.columns + x; +} + diff --git a/Canvas.h b/Canvas.h index e4dc65a..4c959f6 100644 --- a/Canvas.h +++ b/Canvas.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include "CanvasData.h" #include "Tile.h" @@ -9,10 +8,15 @@ class Canvas { public: virtual Tile & get_tile(unsigned x, unsigned y); - virtual void set_tile(unsigned x, unsigned y, std::unique_ptr t); + virtual void set_tile(unsigned x, unsigned y, TileData data); -private: +public: CanvasData data; - std::vector tiles; + +private: + std::vector tiles = {}; + +private: + size_t pos_to_index(unsigned x, unsigned y); }; diff --git a/Deserializer.cpp b/Deserializer.cpp new file mode 100644 index 0000000..ecadbbe --- /dev/null +++ b/Deserializer.cpp @@ -0,0 +1,32 @@ +#include "Deserializer.h" +#include "Exception.h" + +void Deserializer::set_target(Museum * museum) { + this->museum = museum; +} + +Museum & Deserializer::get_target() { + if (this->museum == nullptr) + throw Exception("no museum given to Deserializer"); + + return *this->museum; +} + +void Deserializer::add_artist(ArtistData data) { + Museum & museum = this->get_target(); + // museum.people.add_artist(data); + printf("add artist(%1.2f, %1.2f)...\n", data.x, data.y); +} + +void Deserializer::set_canvas(CanvasData data) { + Museum & museum = this->get_target(); + // museum.canvas.data = data; + printf("set canvas(%dx%d)...\n", data.rows, data.columns); +} + +void Deserializer::add_tile(unsigned int x, unsigned int y, TileData data) { + Museum & museum = this->get_target(); + // museum.canvas.set_tile(x, y, data); + printf("add tile(%d,%d) data(%s)...\n", x, y, data.type.c_str()); +} + diff --git a/Deserializer.h b/Deserializer.h index 5123cc9..78b7920 100644 --- a/Deserializer.h +++ b/Deserializer.h @@ -1,20 +1,25 @@ #pragma once #include -#include #include "Museum.h" #include "ArtistData.h" #include "TileData.h" +#include "CanvasData.h" #include "Color.h" class Deserializer { public: - void set_target(Museum & m); - void add_artist(std::unique_ptr); - void set_rows(unsigned int); - void set_cols(unsigned int); + void set_target(Museum * m); void add_type(std::string, Color, unsigned int); - void add_tile(unsigned int x, unsigned int y, std::unique_ptr); + + void set_canvas(CanvasData); + + void add_tile(unsigned int x, unsigned int y, TileData); + void add_artist(ArtistData); + +private: + Museum & get_target(); + Museum * museum = nullptr; }; diff --git a/LocalFile.cpp b/LocalFile.cpp index 1ff595a..ee5bf74 100644 --- a/LocalFile.cpp +++ b/LocalFile.cpp @@ -11,37 +11,47 @@ void LocalFile::open(const std::string url) { if (path.starts_with(protocol)) path = path.substr(protocol.size()); - _file = new std::ifstream(path, std::ios::in); - if (_file->fail() || !_file->is_open()) + this->file = new std::ifstream(path, std::ios::in); + if (this->file->fail() || !this->file->is_open()) throw Exception("Cannot open file://%s\n", path.c_str()); } void LocalFile::close() { - if (_file == nullptr) return; + if (this->file == nullptr) return; - if (_file->is_open()) - _file->close(); + if (this->file->is_open()) + this->file->close(); } const std::string LocalFile::read() { - if (_file == nullptr) + if (this->content != nullptr) + return *this->content; + + if (this->file == nullptr) throw Exception("File read after destructor\n"); - if (!_file->is_open()) + if (!this->file->is_open()) throw Exception("File read after close\n"); - return std::string( - std::istreambuf_iterator(*_file), + this->content = new std::string( + std::istreambuf_iterator(*this->file), std::istreambuf_iterator() ); + + return *this->content; } LocalFile::~LocalFile() { close(); - if (_file != nullptr) { - delete _file; - _file = nullptr; + if (this->file != nullptr) { + delete this->file; + this->file = nullptr; + } + + if (this->content != nullptr) { + delete this->content; + this->content = nullptr; } } diff --git a/LocalFile.h b/LocalFile.h index df9a479..5a67dab 100644 --- a/LocalFile.h +++ b/LocalFile.h @@ -24,6 +24,7 @@ private: static LocalFile instance; private: - std::ifstream * _file = nullptr; + std::ifstream * file = nullptr; + std::string * content = nullptr; }; diff --git a/Museum.h b/Museum.h index f15a2fe..d7ff57b 100644 --- a/Museum.h +++ b/Museum.h @@ -4,7 +4,7 @@ #include "Canvas.h" class Museum { -private: +public: People people; Canvas canvas; }; diff --git a/Parser.cpp b/Parser.cpp new file mode 100644 index 0000000..8317216 --- /dev/null +++ b/Parser.cpp @@ -0,0 +1,32 @@ +#include + +#include "Parser.h" +#include "Exception.h" + +void Parser::parse(File & file, Deserializer & deserializer) { + auto & col = Parser::get_collection(); + if (col.size() < 1) + throw Exception("no parsers registered"); + + unsigned int best_score = 0; + ParserStrategy * best_strategy = nullptr; + for (ParserStrategy * strategy : col) { + unsigned int score = strategy->heuristic(file); + if (score <= best_score) continue; + + best_score = score; + best_strategy = strategy; + } + + if (best_strategy == nullptr) + throw Exception("unknown file type"); + + best_strategy->parse(file, deserializer); +} + +void Parser::register_strategy(ParserStrategy * ps) { + auto & col = Parser::get_collection(); + if (std::find(col.begin(), col.end(), ps) != col.end()) return; + col.push_back(ps); +} + diff --git a/Parser.h b/Parser.h index c867f9e..5ed68f6 100644 --- a/Parser.h +++ b/Parser.h @@ -5,10 +5,16 @@ #include "ParserStrategy.h" class Parser { + typedef std::vector ParserCollection; + public: static void parse(File & f, Deserializer & d); + static void register_strategy(ParserStrategy * p); private: - static void register_strategy(ParserStrategy & p); + static ParserCollection & get_collection() { + static ParserCollection c = {}; + return c; + } }; diff --git a/ParserStrategy.h b/ParserStrategy.h index c9f9eff..54e853a 100644 --- a/ParserStrategy.h +++ b/ParserStrategy.h @@ -4,10 +4,6 @@ #include "Deserializer.h" class ParserStrategy { -public: - ParserStrategy() = default; - virtual ~ParserStrategy() = 0; - public: virtual void parse(File & f, Deserializer & d) = 0; virtual unsigned int heuristic(File & f) = 0; diff --git a/People.h b/People.h index ff30595..e4490f7 100644 --- a/People.h +++ b/People.h @@ -8,7 +8,7 @@ class People { public: - void add_artist(std::unique_ptr data); + void add_artist(ArtistData data); private: std::vector artists; diff --git a/TXTParser.cpp b/TXTParser.cpp new file mode 100644 index 0000000..c9de4ca --- /dev/null +++ b/TXTParser.cpp @@ -0,0 +1,16 @@ +#include "TXTParser.h" +#include "Parser.h" + +TXTParser TXTParser::instance {}; +TXTParser::TXTParser() { + Parser::register_strategy(this); +} + +unsigned int TXTParser::heuristic(File & f) { + return 0; +} + +void TXTParser::parse(File & f, Deserializer & d) { + printf("%s\n", __PRETTY_FUNCTION__); +} + diff --git a/TXTParser.h b/TXTParser.h index 3f59c93..1db9e9d 100644 --- a/TXTParser.h +++ b/TXTParser.h @@ -1,2 +1,14 @@ #pragma once +#include "ParserStrategy.h" + +class TXTParser : public ParserStrategy { +public: + virtual void parse(File & f, Deserializer & d); + virtual unsigned int heuristic(File & f); + +private: + static TXTParser instance; + TXTParser(); +}; + diff --git a/Tile.cpp b/Tile.cpp new file mode 100644 index 0000000..b461bdf --- /dev/null +++ b/Tile.cpp @@ -0,0 +1,6 @@ +#include "Tile.h" + +Tile::Tile(TileData data) { + this->data = data; +} + diff --git a/Tile.h b/Tile.h index d4977de..954709f 100644 --- a/Tile.h +++ b/Tile.h @@ -5,6 +5,10 @@ #include "TileAppearance.h" class Tile { +public: + Tile(); + Tile(TileData data); + public: TileData data; diff --git a/TileAppearance.h b/TileAppearance.h index 3f59c93..25db909 100644 --- a/TileAppearance.h +++ b/TileAppearance.h @@ -1,2 +1,9 @@ #pragma once +#include "Color.h" + +class TileAppearance { +public: + Color color; +}; + diff --git a/TileBehavior.h b/TileBehavior.h index 3f59c93..e46c0cf 100644 --- a/TileBehavior.h +++ b/TileBehavior.h @@ -1,2 +1,6 @@ #pragma once +class TileBehavior { + +}; + diff --git a/XMLParser.cpp b/XMLParser.cpp new file mode 100644 index 0000000..779f51f --- /dev/null +++ b/XMLParser.cpp @@ -0,0 +1,29 @@ +#include + +#include "XMLParser.h" +#include "Parser.h" + +using namespace std; + +XMLParser XMLParser::instance {}; +XMLParser::XMLParser() { + Parser::register_strategy(this); +} + +unsigned int XMLParser::heuristic(File & f) { + const string content = f.read(); + int open_backets = 0; + int close_brackets = 0; + for (char c : content) { + if (c == '<') open_backets++; + if (c == '>') close_brackets++; + } + int balance = abs(open_backets - close_brackets); + int penalty = 1 + balance * 10; + return (open_backets + close_brackets) / penalty; +} + +void XMLParser::parse(File & f, Deserializer & d) { + printf("%s\n", __PRETTY_FUNCTION__); +} + diff --git a/XMLParser.h b/XMLParser.h index 3f59c93..7e3ff09 100644 --- a/XMLParser.h +++ b/XMLParser.h @@ -1,2 +1,14 @@ #pragma once +#include "ParserStrategy.h" + +class XMLParser : public ParserStrategy { +public: + virtual void parse(File & f, Deserializer & d); + virtual unsigned int heuristic(File & f); + +private: + static XMLParser instance; + XMLParser(); +}; + diff --git a/main.cpp b/main.cpp index 9e11e5b..b258a49 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,6 @@ #include +#include "Exception.h" #include "FileReader.h" #include "Museum.h" #include "Deserializer.h" @@ -9,12 +10,29 @@ int main(int argc, char** argv) { Museum m {}; Deserializer d {}; + d.set_target(&m); + for (int i = 1; i < argc; i++) { - File & f = FileReader::open(argv[i]); - Parser::parse(f, d); - f.close(); + char * url = argv[i]; + File * f = nullptr; + + try { + f = &FileReader::open(url); + } catch (Exception & e) { + printf("File open error: %s\n", e.what()); + return EXIT_FAILURE; + } + + try { + Parser::parse(*f, d); + } catch (Exception & e) { + printf("Parser error: %s (%s)\n", e.what(), url); + return EXIT_FAILURE; + } + + f->close(); } - return 0; + return EXIT_SUCCESS; } -- cgit v1.2.3