// ============================================================== // This file is part of MegaGlest Shared Library (www.megaglest.org) // // Copyright (C) 2012 Mark Vejvoda, Titus Tscharntke // The MegaGlest Team, under GNU GPL v3.0 // ============================================================== #ifndef FILE_READER_H #define FILE_READER_H #include "platform_util.h" #include #include #include #include #include #include #include #include #include "conversion.h" #include "leak_dumper.h" using std::map; using std::string; using std::vector; using std::ifstream; using std::ios; using std::runtime_error; using namespace Shared::Util; using Shared::PlatformCommon::extractExtension; #define AS_STRING(...) #__VA_ARGS__ namespace Shared{ // ===================================================== // class FileReader // ===================================================== template class FileReader { public: //string const * extensions; std::vector extensions; /**Creates a filereader being able to possibly load files * from the specified extension **/ //FileReader(string const * extensions); FileReader(std::vector extensions); /**Creates a low-priority filereader **/ FileReader(); public: /*Return the - existing and initialized - fileReadersMap */ static map const * >* >& getFileReadersMap() { static map const * >* > fileReaderByExtension; return fileReaderByExtension; } static vector const * >& getFileReaders() { static vector const*> fileReaders; return fileReaders; }; static vector const * >& getLowPriorityFileReaders() { static vector const*> lowPriorityFileReaders; return lowPriorityFileReaders; }; public: /**Tries to read a file * This method tries to read the file with the specified filepath. * If it fails, either null is returned or an exception * is thrown*/ static T* readPath(const string& filepath); /**Tries to read a file from an object * This method tries to read the file with the specified filepath. * If it fails, either null is returned or an exception * is thrown*/ static T* readPath(const string& filepath, T* object); /**Gives a quick estimation of whether the specified file * can be read or not depending on the filename*/ virtual bool canRead(const string& filepath) const; /**Gives a better estimation of whether the specified file * can be read or not depending on the file content*/ virtual bool canRead(ifstream& file) const; virtual void cleanupExtensions(); /**Reads a file * This method tries to read the file with the specified filepath * If it fails, either null is returned or an exception * is thrown */ virtual T* read(const string& filepath) const; /**Reads a file to an object * This method tries to read the file with the specified filepath * If it fails, either null is returned or an exception * is thrown */ virtual T* read(const string& filepath, T* object) const; /**Reads a file * This method tries to read the specified file * If it failes, either null is returned or an exception * Default implementation generates an object using T() * is thrown */ virtual T* read(ifstream& file, const string& path) const { T* obj = new T(); T* ret = read(file,path,obj); if (obj != ret) { delete obj; } return ret; } /**Reads a file onto the specified object * This method tries to read the specified file * If it failes, either null is returned or an exception * is thrown */ virtual T* read(ifstream& file, const string& path, T* former) const = 0; virtual ~FileReader() { cleanupExtensions(); }; //Well ... these objects aren't supposed to be destroyed }; template static inline T* readFromFileReaders(vector const *>* readers, const string& filepath) { //try to assign file #if defined(WIN32) && !defined(__MINGW32__) FILE *fp = _wfopen(utf8_decode(filepath).c_str(), L"rb"); ifstream file(fp); #else ifstream file(filepath.c_str(), ios::in | ios::binary); #endif if (!file.is_open()) { throw megaglest_runtime_error("[#1] Could not open file " + filepath); } for (typename vector const *>::const_iterator i = readers->begin(); i != readers->end(); ++i) { T* ret = NULL; file.seekg(0, ios::beg); //Set position to first try { FileReader const * reader = *i; ret = reader->read(file, filepath); //It is guaranteed that at least the filepath matches ... } #if defined(WIN32) catch (megaglest_runtime_error) { #else catch (megaglest_runtime_error &ex) { #endif throw; } catch (...) { continue; } if (ret != NULL) { file.close(); #if defined(WIN32) && !defined(__MINGW32__) if(fp) fclose(fp); #endif return ret; } } file.close(); #if defined(WIN32) && !defined(__MINGW32__) if(fp) fclose(fp); #endif return NULL; } template static inline T* readFromFileReaders(vector const *>* readers, const string& filepath, T* object) { //try to assign file #if defined(WIN32) && !defined(__MINGW32__) wstring wstr = utf8_decode(filepath); FILE *fp = _wfopen(wstr.c_str(), L"rb"); int fileErrno = errno; ifstream file(fp); #else ifstream file(filepath.c_str(), ios::in | ios::binary); #endif if (!file.is_open()) { #if defined(WIN32) && !defined(__MINGW32__) DWORD error = GetLastError(); throw megaglest_runtime_error("[#2] Could not open file, result: " + intToStr(error) + " - " + intToStr(fileErrno) + " [" + filepath + "]"); #else throw megaglest_runtime_error("[#2] Could not open file [" + filepath + "]"); #endif } for (typename vector const *>::const_iterator i = readers->begin(); i != readers->end(); ++i) { T* ret = NULL; file.seekg(0, ios::beg); //Set position to first try { FileReader const * reader = *i; ret = reader->read(file, filepath, object); //It is guaranteed that at least the filepath matches ... } #if defined(WIN32) catch (megaglest_runtime_error) { #else catch (megaglest_runtime_error &ex) { #endif throw; } catch (...) { continue; } if (ret != NULL) { file.close(); #if defined(WIN32) && !defined(__MINGW32__) if(fp) fclose(fp); #endif return ret; } } file.close(); #if defined(WIN32) && !defined(__MINGW32__) if(fp) fclose(fp); #endif return NULL; } /**Tries to read a file * This method tries to read the file with the specified filepath. * If it fails, either null is returned or an exception * is thrown*/ template T* FileReader::readPath(const string& filepath) { const string& extension = extractExtension(filepath); vector const * >* possibleReaders = (getFileReadersMap())[extension]; if (possibleReaders != NULL) { //Search in these possible readers T* ret = readFromFileReaders(possibleReaders, filepath); if (ret != NULL) { return ret; } } T* ret = readFromFileReaders(&(getFileReaders()), filepath); //Try all other if (ret == NULL) { std::cerr << "ERROR #1 - Could not parse filepath: " << filepath << std::endl; throw megaglest_runtime_error(string("Could not parse ") + filepath + " as object of type " + typeid(T).name()); } return ret; } /**Tries to read a file * This method tries to read the file with the specified filepath. * If it fails, either null is returned or an exception * is thrown*/ template T* FileReader::readPath(const string& filepath, T* object) { const string& extension = extractExtension(filepath); vector const * >* possibleReaders = (getFileReadersMap())[extension]; if (possibleReaders != NULL) { //Search in these possible readers T* ret = readFromFileReaders(possibleReaders, filepath, object); if (ret != NULL) { return ret; } } T* ret = readFromFileReaders(&(getFileReaders()), filepath, object); //Try all other if (ret == NULL) { std::cerr << "ERROR #2 - Could not parse filepath: " << filepath << std::endl; ret = readFromFileReaders(&(getLowPriorityFileReaders()), filepath); //Try to get dummy file if (ret == NULL) { throw megaglest_runtime_error(string("Could not parse ") + filepath + " as object of type " + typeid(T).name()); } } return ret; } template FileReader::FileReader(std::vector extensions): extensions(extensions) { getFileReaders().push_back(this); std::vector nextExtension = extensions; for(unsigned int i = 0; i < nextExtension.size(); ++i) { vector const* >* curPossibleReaders = (getFileReadersMap())[nextExtension[i]]; if (curPossibleReaders == NULL) { (getFileReadersMap())[nextExtension[i]] = (curPossibleReaders = new vector const *>()); } curPossibleReaders->push_back(this); } } template void FileReader::cleanupExtensions() { std::vector nextExtension = extensions; for(unsigned int i = 0; i < nextExtension.size(); ++i) { vector const* >* curPossibleReaders = (getFileReadersMap())[nextExtension[i]]; if (curPossibleReaders != NULL) { delete curPossibleReaders; (getFileReadersMap())[nextExtension[i]] = NULL; } } } /**Gives a quick estimation of whether the specified file * can be read or not depending on the filename*/ template bool FileReader::canRead(const string& filepath) const { const string& realExtension = extractExtension(filepath); //const string* haveExtension = extensions; std::vector haveExtension = extensions; //while (*haveExtension != "") { for(unsigned int i = 0; i < haveExtension.size(); ++i) { //if (realExtension == *haveExtension) { if (realExtension == haveExtension[i]) { return true; } //++haveExtension; } return false; } /**Gives a better estimation of whether the specified file * can be read or not depending on the file content*/ template bool FileReader::canRead(ifstream& file) const { try { T* wouldRead = read(file,"unknown file"); bool ret = (wouldRead != NULL); delete wouldRead; return ret; } #if defined(WIN32) catch (megaglest_runtime_error) { #else catch (megaglest_runtime_error &ex) { #endif throw; } catch (...) { return false; } } /**Reads a file * This method tries to read the file with the specified filepath * If it fails, either null is returned or an exception * is thrown */ template T* FileReader::read(const string& filepath) const { #if defined(WIN32) && !defined(__MINGW32__) FILE *fp = _wfopen(utf8_decode(filepath).c_str(), L"rb"); ifstream file(fp); #else ifstream file(filepath.c_str(), ios::in | ios::binary); #endif if (!file.is_open()) { throw megaglest_runtime_error("[#3] Could not open file " + filepath); } T* ret = read(file,filepath); file.close(); #if defined(WIN32) && !defined(__MINGW32__) if(fp) fclose(fp); #endif return ret; } /**Reads a file * This method tries to read the file with the specified filepath * If it fails, either null is returned or an exception * is thrown */ template T* FileReader::read(const string& filepath, T* object) const { #if defined(WIN32) && !defined(__MINGW32__) FILE *fp = _wfopen(utf8_decode(filepath).c_str(), L"rb"); ifstream file(fp); #else ifstream file(filepath.c_str(), ios::in | ios::binary); #endif if (!file.is_open()) { throw megaglest_runtime_error("[#4] Could not open file " + filepath); } T* ret = read(file,filepath,object); file.close(); #if defined(WIN32) && !defined(__MINGW32__) if(fp) fclose(fp); #endif return ret; } } //end namespace #endif