diff options
-rw-r--r-- | Artist.h | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | CSVParser.cpp | 82 | ||||
-rw-r--r-- | CSVParser.h | 12 | ||||
-rw-r--r-- | Canvas.cpp | 14 | ||||
-rw-r--r-- | Canvas.h | 12 | ||||
-rw-r--r-- | Deserializer.cpp | 32 | ||||
-rw-r--r-- | Deserializer.h | 17 | ||||
-rw-r--r-- | LocalFile.cpp | 34 | ||||
-rw-r--r-- | LocalFile.h | 3 | ||||
-rw-r--r-- | Museum.h | 2 | ||||
-rw-r--r-- | Parser.cpp | 32 | ||||
-rw-r--r-- | Parser.h | 8 | ||||
-rw-r--r-- | ParserStrategy.h | 4 | ||||
-rw-r--r-- | People.h | 2 | ||||
-rw-r--r-- | TXTParser.cpp | 16 | ||||
-rw-r--r-- | TXTParser.h | 12 | ||||
-rw-r--r-- | Tile.cpp | 6 | ||||
-rw-r--r-- | Tile.h | 4 | ||||
-rw-r--r-- | TileAppearance.h | 7 | ||||
-rw-r--r-- | TileBehavior.h | 4 | ||||
-rw-r--r-- | XMLParser.cpp | 29 | ||||
-rw-r--r-- | XMLParser.h | 12 | ||||
-rw-r--r-- | main.cpp | 26 |
24 files changed, 345 insertions, 35 deletions
@@ -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 <cstdlib> +#include <string> +#include <sstream> +#include <vector> +#include <algorithm> + +#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<string> 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<vector<string>> 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<string> 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<string> 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<string> 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; +} + @@ -1,7 +1,6 @@ #pragma once #include <vector> -#include <memory> #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<TileData> t); + virtual void set_tile(unsigned x, unsigned y, TileData data); -private: +public: CanvasData data; - std::vector<Tile> tiles; + +private: + std::vector<Tile> 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 <string> -#include <memory> #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<ArtistData>); - 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<TileData>); + + 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<char>(*_file), + this->content = new std::string( + std::istreambuf_iterator<char>(*this->file), std::istreambuf_iterator<char>() ); + + 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; }; @@ -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 <algorithm> + +#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); +} + @@ -5,10 +5,16 @@ #include "ParserStrategy.h" class Parser { + typedef std::vector<ParserStrategy*> 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 @@ -5,10 +5,6 @@ class ParserStrategy { public: - ParserStrategy() = default; - virtual ~ParserStrategy() = 0; - -public: virtual void parse(File & f, Deserializer & d) = 0; virtual unsigned int heuristic(File & f) = 0; }; @@ -8,7 +8,7 @@ class People { public: - void add_artist(std::unique_ptr<ArtistData> data); + void add_artist(ArtistData data); private: std::vector<Artist> 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; +} + @@ -6,6 +6,10 @@ class Tile { public: + Tile(); + Tile(TileData data); + +public: TileData data; private: 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 <cstdlib> + +#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(); +}; + @@ -1,5 +1,6 @@ #include <cstdio> +#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; } |