appsystem.hpp (10237B)
1 #ifndef APPSYSTEM_HPP 2 #define APPSYSTEM_HPP 3 4 #include <algorithm> 5 #include <fstream> 6 #include <iostream> 7 #include <sstream> 8 9 #include "errlog.hpp" 10 #include "game.hpp" 11 #include "office.hpp" 12 13 class AppSystem 14 { 15 private: 16 std::vector<App *> apps; 17 std::vector<Manufacturer *> manfs; 18 ErrLog errlog; 19 20 public: 21 AppSystem(); 22 ~AppSystem(); 23 24 AppSystem& operator+= (App *app); 25 AppSystem& operator+= (Manufacturer *manf); 26 27 template<typename T> void import_data(const char *fpath); 28 template<typename T> void export_data(const char *fpath); 29 template<typename T> void call( 30 std::string&& appname, 31 const T element, 32 void (App::*setter)(T)); 33 template<typename T, class U> void cast_call( 34 std::string&& appname, 35 const T element, 36 void (U::*setter)(T)); 37 void removebad(const Manufacturer *manf); 38 void removebad(const char *manfsn); 39 40 constexpr const std::vector<App *>& get_apps() const {return apps;} 41 constexpr const std::vector<Manufacturer *>& get_manfs() const {return manfs;} 42 const std::vector<Office *> get_freeapps() const; 43 const std::vector<Game *> get_goodgames() const; 44 45 private: 46 template<typename T> bool exists(const std::vector<T *>& vec, const T *element); 47 template<typename T> void parse(std::ifstream& f); 48 const std::vector<std::string> parse_office_exts(std::ifstream& f); 49 const std::vector<Review *> 50 parse_reviews(const std::string& appname, const char *rpath); 51 void write_office_exts(const Office *of, std::ofstream& f); 52 const std::string err_read(const char *fpath); 53 const std::string err_write(const char *fpath); 54 template<typename T> void dealloc(std::vector<T *>& vec); 55 }; 56 57 /* 58 * Parses .csv fields for each type T 59 * The function is templated just for the sake 60 * of not having overloads, although this one is 61 * not any more practical either. 62 */ 63 template<typename T> void 64 AppSystem::parse(std::ifstream& f) 65 { 66 if constexpr (std::is_same_v<T, Manufacturer>) 67 { 68 std::string sn, name, email; 69 std::getline(f, sn, ','); 70 std::getline(f, name, ','); 71 std::getline(f, email); 72 if (f.eof()) return; 73 manfs.push_back(new Manufacturer(sn.c_str(), name.c_str(), email)); 74 } 75 else if constexpr (std::is_same_v<T, Office>) 76 { 77 std::string sn, name, os, manf, price, skip1, skip2; 78 std::getline(f, sn, ','); 79 std::getline(f, name, ','); 80 std::getline(f, os, ','); 81 std::getline(f, manf, ','); 82 std::getline(f, price, ','); 83 std::getline(f, skip1, ','); 84 std::getline(f, skip2, ','); 85 if (f.eof()) return; 86 std::vector<std::string> exts = parse_office_exts(f); 87 88 if (!manfs.empty()) 89 { 90 for (const auto& man : manfs) 91 { 92 if (man->get_name() == manf) 93 { 94 apps.push_back(new Office(sn.c_str(), name, os, 95 man, std::stoi(price), exts)); 96 break; 97 } 98 } 99 } 100 } 101 else if constexpr (std::is_same_v<T, Game>) 102 { 103 std::string sn, name, os, manf, price, genre, online; 104 std::string skip; 105 std::getline(f, sn, ','); 106 std::getline(f, name, ','); 107 std::getline(f, os, ','); 108 std::getline(f, manf, ','); 109 std::getline(f, price, ','); 110 std::getline(f, genre, ','); 111 std::getline(f, online, ','); 112 std::getline(f, skip); 113 if (f.eof()) return; 114 bool onl = online == "Yes"; 115 116 if (!manfs.empty()) 117 { 118 for (const auto& man : manfs) 119 { 120 if (man->get_name() == manf) 121 { 122 apps.push_back(new Game(sn.c_str(), name, os, 123 man, std::stoi(price), genre, onl)); 124 break; 125 } 126 } 127 } 128 } 129 else if constexpr (std::is_same_v<T, Review>) 130 { 131 std::string appname, stars, username, comment; 132 std::getline(f, appname, ','); 133 std::getline(f, stars, ','); 134 std::getline(f, username, ','); 135 std::getline(f, comment); 136 if (f.eof()) return; 137 for (auto&& app : apps) 138 if (appname == app->get_name()) 139 app->addrev(new Review(std::stoi(stars), username, comment)); 140 } 141 } 142 143 /* 144 * Imports data from a given path and handles a possible 145 * std::ifstream::badbit exception 146 */ 147 template<typename T> void 148 AppSystem::import_data(const char *fpath) 149 { 150 std::ifstream f; 151 f.exceptions(std::ifstream::badbit); 152 try 153 { 154 std::string strpath(fpath); 155 f.open(fpath); 156 if (f.is_open()) 157 { 158 std::printf("Importing data from \'%s\'\n", fpath); 159 std::string skip; 160 std::getline(f, skip); 161 while (f.good()) 162 { 163 if constexpr (std::is_same_v<T, Manufacturer>) 164 parse<Manufacturer>(f); 165 else if constexpr (std::is_same_v<T, App>) 166 { 167 std::string type; 168 std::getline(f, type, ','); 169 if (type == "Game") parse<Game>(f); 170 if (type == "Office") parse<Office>(f); 171 } 172 else if constexpr (std::is_same_v<T, Review>) 173 parse<Review>(f); 174 } 175 } 176 f.close(); 177 } 178 catch (const std::ifstream::failure& e) 179 { 180 /* 181 * We terminate because there's no point in continuing if 182 * the file is corrupted 183 */ 184 errlog.write(err_read(fpath) + " (" + e.what() + ")"); 185 throw std::runtime_error(err_read(fpath) + " (" + e.what() + ")"); 186 } 187 } 188 189 /* 190 * Exports data to a given path for each type T. 191 * This function is also templated just for the sake 192 * of not having overloads. It also handles exceptions. 193 */ 194 template<typename T> void 195 AppSystem::export_data(const char *fpath) 196 { 197 std::ofstream f; 198 f.exceptions(std::ofstream::failbit | std::ofstream::badbit); 199 try 200 { 201 std::string strpath(fpath); 202 f.open(fpath); 203 std::printf("Exporting data to \'%s\'\n", fpath); 204 205 if constexpr (std::is_same_v<T, Manufacturer>) 206 { 207 f << "SN,Name,Email\n"; 208 for (const auto& manf : manfs) 209 f << manf->get_serialnum() << ',' << 210 manf->get_name() << ',' << 211 manf->get_email() << std::endl; 212 } 213 else if constexpr (std::is_same_v<T, App>) 214 { 215 f << "Type,SN,Name,OS,Manf,Price,Genre,Online,Extensions\n"; 216 for (const auto& app : apps) 217 { 218 Manufacturer manf = app->get_manf(); 219 Game *o = dynamic_cast<Game *>(app); 220 f << (o ? "Game" : "Office") << ','; 221 f << app->get_serialnum() << ',' << 222 app->get_name() << ',' << 223 app->get_os() << ',' << 224 manf.get_name() << ',' << 225 app->get_price() << ',' << 226 (o ? o->get_genre() :"N/A") << ',' << 227 (o ? (o->get_online() ? "Yes" : "No") : "N/A") << ','; 228 if (o) f << "N/A" << std::endl; 229 else 230 { 231 Office *of = dynamic_cast<Office *>(app); 232 write_office_exts(of, f); 233 f << std::endl; 234 } 235 } 236 } 237 else if constexpr (std::is_same_v<T, Review>) 238 { 239 f << "AppName,Stars,Username,Comment\n"; 240 for (const auto& app : apps) 241 { 242 const std::vector<Review *> revs = app->get_revs(); 243 if (!revs.empty()) 244 for (const auto& rev : revs) 245 f << 246 app->get_name() << ',' << 247 rev->get_stars() << ',' << 248 rev->get_username() << ',' << 249 rev->get_comment() << std::endl; 250 } 251 } 252 f.close(); 253 } 254 catch (const std::ofstream::failure& e) 255 { 256 /* 257 * In this case we don't terminate because the output 258 * file is not to be used by the program later, so 259 * we shouldn't lose all progress. 260 */ 261 errlog.write(err_write(fpath) + " (" + e.what() + ")"); 262 } 263 } 264 265 /* 266 * Searches a vector of any type T and returns true if the 267 * element is found. I made it just so I don't have to write 268 * all this every time. 269 */ 270 template<typename T> bool 271 AppSystem::exists(const std::vector<T *>& vec, const T *element) 272 { 273 return std::find(vec.begin(), vec.end(), element) != vec.end(); 274 } 275 276 /* 277 * Searches through the apps array and in case the app is found 278 * it calls a given App setter through a function pointer. 279 * "element" must be of the same type as the setter's parameter. 280 * It's ugly. 281 */ 282 template<typename T> void 283 AppSystem::call(std::string&& appname, const T element, void (App::*setter)(T)) 284 { 285 for (auto&& app : apps) 286 if (app->get_name() == appname) 287 (app->*setter)(element); 288 } 289 290 /* 291 * Same as above but this one is used for the derived classes 292 * "Game" and "Office". I could use this one in all cases but 293 * dynamic casting everytime would be pointless. 294 */ 295 template<typename T, class U> void 296 AppSystem::cast_call(std::string&& appname, const T element, void (U::*setter)(T)) 297 { 298 for (auto&& app : apps) 299 if (U *o = dynamic_cast<U *>(app)) 300 if (o->get_name() == appname) 301 (o->*setter)(element); 302 } 303 304 /* 305 * Frees every pointer object in every vector. 306 * This function is called only by the destructor. 307 */ 308 template<typename T> void 309 AppSystem::dealloc(std::vector<T *>& vec) 310 { 311 if (!vec.empty()) 312 { 313 for (auto&& v : vec) 314 if (v != nullptr) 315 delete v; 316 vec.clear(); 317 } 318 } 319 320 #endif /* APPSYSTEM_HPP */