1 // ==============================================================
2 // This file is part of MegaGlest Shared Library (www.megaglest.org)
3 //
4 // Copyright (C) 2012 Mark Vejvoda, Titus Tscharntke
5 // The MegaGlest Team, under GNU GPL v3.0
6 // ==============================================================
7
8 #ifndef FILE_READER_H
9 #define FILE_READER_H
10
11 #include "platform_util.h"
12 #include <map>
13 #include <string>
14 #include <vector>
15 #include <fstream>
16 #include <iostream>
17 #include <stdexcept>
18 #include <typeinfo>
19 #include <vector>
20 #include "conversion.h"
21 #include "leak_dumper.h"
22
23 using std::map;
24 using std::string;
25 using std::vector;
26 using std::ifstream;
27 using std::ios;
28 using std::runtime_error;
29 using namespace Shared::Util;
30
31 using Shared::PlatformCommon::extractExtension;
32
33 #define AS_STRING(...) #__VA_ARGS__
34
35 namespace Shared{
36
37 // =====================================================
38 // class FileReader
39 // =====================================================
40
41 template <class T>
42 class FileReader {
43 public:
44 //string const * extensions;
45 std::vector<string> extensions;
46
47 /**Creates a filereader being able to possibly load files
48 * from the specified extension
49 **/
50 //FileReader(string const * extensions);
51 FileReader(std::vector<string> extensions);
52
53 /**Creates a low-priority filereader
54 **/
55 FileReader();
56
57 public:
58 /*Return the - existing and initialized - fileReadersMap
59 */
getFileReadersMap()60 static map<string, vector<FileReader<T> const * >* >& getFileReadersMap() {
61 static map<string, vector<FileReader<T> const * >* > fileReaderByExtension;
62 return fileReaderByExtension;
63 }
64
getFileReaders()65 static vector<FileReader<T> const * >& getFileReaders() {
66 static vector<FileReader<T> const*> fileReaders;
67 return fileReaders;
68 };
69
getLowPriorityFileReaders()70 static vector<FileReader<T> const * >& getLowPriorityFileReaders() {
71 static vector<FileReader<T> const*> lowPriorityFileReaders;
72 return lowPriorityFileReaders;
73 };
74
75
76 public:
77
78 /**Tries to read a file
79 * This method tries to read the file with the specified filepath.
80 * If it fails, either <code>null</code> is returned or an exception
81 * is thrown*/
82 static T* readPath(const string& filepath);
83
84 /**Tries to read a file from an object
85 * This method tries to read the file with the specified filepath.
86 * If it fails, either <code>null</code> is returned or an exception
87 * is thrown*/
88 static T* readPath(const string& filepath, T* object);
89
90 /**Gives a quick estimation of whether the specified file
91 * can be read or not depending on the filename*/
92 virtual bool canRead(const string& filepath) const;
93
94 /**Gives a better estimation of whether the specified file
95 * can be read or not depending on the file content*/
96 virtual bool canRead(ifstream& file) const;
97
98 virtual void cleanupExtensions();
99
100 /**Reads a file
101 * This method tries to read the file with the specified filepath
102 * If it fails, either <code>null</code> is returned or an exception
103 * is thrown
104 */
105 virtual T* read(const string& filepath) const;
106
107 /**Reads a file to an object
108 * This method tries to read the file with the specified filepath
109 * If it fails, either <code>null</code> is returned or an exception
110 * is thrown
111 */
112 virtual T* read(const string& filepath, T* object) const;
113
114 /**Reads a file
115 * This method tries to read the specified file
116 * If it failes, either <code>null</code> is returned or an exception
117 * Default implementation generates an object using T()
118 * is thrown
119 */
read(ifstream & file,const string & path)120 virtual T* read(ifstream& file, const string& path) const {
121 T* obj = new T();
122 T* ret = read(file,path,obj);
123 if (obj != ret) {
124 delete obj;
125 }
126 return ret;
127 }
128
129
130 /**Reads a file onto the specified object
131 * This method tries to read the specified file
132 * If it failes, either <code>null</code> is returned or an exception
133 * is thrown
134 */
135 virtual T* read(ifstream& file, const string& path, T* former) const = 0;
136
~FileReader()137 virtual ~FileReader() {
138 cleanupExtensions();
139 }; //Well ... these objects aren't supposed to be destroyed
140 };
141
142 template <typename T>
readFromFileReaders(vector<FileReader<T> const * > * readers,const string & filepath)143 static inline T* readFromFileReaders(vector<FileReader<T> const *>* readers, const string& filepath) {
144 //try to assign file
145 #if defined(WIN32) && !defined(__MINGW32__)
146 FILE *fp = _wfopen(utf8_decode(filepath).c_str(), L"rb");
147 ifstream file(fp);
148 #else
149 ifstream file(filepath.c_str(), ios::in | ios::binary);
150 #endif
151 if (!file.is_open()) {
152 throw megaglest_runtime_error("[#1] Could not open file " + filepath);
153 }
154 for (typename vector<FileReader<T> const *>::const_iterator i = readers->begin(); i != readers->end(); ++i) {
155 T* ret = NULL;
156 file.seekg(0, ios::beg); //Set position to first
157 try {
158 FileReader<T> const * reader = *i;
159 ret = reader->read(file, filepath); //It is guaranteed that at least the filepath matches ...
160 }
161 #if defined(WIN32)
162 catch (megaglest_runtime_error) {
163 #else
164 catch (megaglest_runtime_error &ex) {
165 #endif
166 throw;
167 }
168 catch (...) {
169 continue;
170 }
171 if (ret != NULL) {
172 file.close();
173 #if defined(WIN32) && !defined(__MINGW32__)
174 if(fp) fclose(fp);
175 #endif
176 return ret;
177 }
178 }
179 file.close();
180 #if defined(WIN32) && !defined(__MINGW32__)
181 if(fp) fclose(fp);
182 #endif
183
184 return NULL;
185 }
186
187 template <typename T>
188 static inline T* readFromFileReaders(vector<FileReader<T> const *>* readers, const string& filepath, T* object) {
189 //try to assign file
190 #if defined(WIN32) && !defined(__MINGW32__)
191 wstring wstr = utf8_decode(filepath);
192 FILE *fp = _wfopen(wstr.c_str(), L"rb");
193 int fileErrno = errno;
194 ifstream file(fp);
195 #else
196 ifstream file(filepath.c_str(), ios::in | ios::binary);
197 #endif
198 if (!file.is_open()) {
199 #if defined(WIN32) && !defined(__MINGW32__)
200 DWORD error = GetLastError();
201 throw megaglest_runtime_error("[#2] Could not open file, result: " + intToStr(error) + " - " + intToStr(fileErrno) + " [" + filepath + "]");
202 #else
203 throw megaglest_runtime_error("[#2] Could not open file [" + filepath + "]");
204 #endif
205 }
206 for (typename vector<FileReader<T> const *>::const_iterator i = readers->begin(); i != readers->end(); ++i) {
207 T* ret = NULL;
208 file.seekg(0, ios::beg); //Set position to first
209 try {
210 FileReader<T> const * reader = *i;
211 ret = reader->read(file, filepath, object); //It is guaranteed that at least the filepath matches ...
212 }
213 #if defined(WIN32)
214 catch (megaglest_runtime_error) {
215 #else
216 catch (megaglest_runtime_error &ex) {
217 #endif
218 throw;
219 }
220 catch (...) {
221 continue;
222 }
223 if (ret != NULL) {
224 file.close();
225 #if defined(WIN32) && !defined(__MINGW32__)
226 if(fp) fclose(fp);
227 #endif
228 return ret;
229 }
230 }
231 file.close();
232 #if defined(WIN32) && !defined(__MINGW32__)
233 if(fp) fclose(fp);
234 #endif
235 return NULL;
236 }
237
238 /**Tries to read a file
239 * This method tries to read the file with the specified filepath.
240 * If it fails, either <code>null</code> is returned or an exception
241 * is thrown*/
242 template <typename T>
243 T* FileReader<T>::readPath(const string& filepath) {
244 const string& extension = extractExtension(filepath);
245 vector<FileReader<T> const * >* possibleReaders = (getFileReadersMap())[extension];
246 if (possibleReaders != NULL) {
247 //Search in these possible readers
248 T* ret = readFromFileReaders(possibleReaders, filepath);
249 if (ret != NULL) {
250 return ret;
251 }
252 }
253 T* ret = readFromFileReaders(&(getFileReaders()), filepath); //Try all other
254 if (ret == NULL) {
255 std::cerr << "ERROR #1 - Could not parse filepath: " << filepath << std::endl;
256 throw megaglest_runtime_error(string("Could not parse ") + filepath + " as object of type " + typeid(T).name());
257 }
258 return ret;
259 }
260
261 /**Tries to read a file
262 * This method tries to read the file with the specified filepath.
263 * If it fails, either <code>null</code> is returned or an exception
264 * is thrown*/
265 template <typename T>
266 T* FileReader<T>::readPath(const string& filepath, T* object) {
267 const string& extension = extractExtension(filepath);
268 vector<FileReader<T> const * >* possibleReaders = (getFileReadersMap())[extension];
269 if (possibleReaders != NULL) {
270 //Search in these possible readers
271 T* ret = readFromFileReaders(possibleReaders, filepath, object);
272 if (ret != NULL) {
273 return ret;
274 }
275 }
276 T* ret = readFromFileReaders(&(getFileReaders()), filepath, object); //Try all other
277 if (ret == NULL) {
278 std::cerr << "ERROR #2 - Could not parse filepath: " << filepath << std::endl;
279 ret = readFromFileReaders(&(getLowPriorityFileReaders()), filepath); //Try to get dummy file
280 if (ret == NULL) {
281 throw megaglest_runtime_error(string("Could not parse ") + filepath + " as object of type " + typeid(T).name());
282 }
283 }
284 return ret;
285 }
286
287 template <typename T>
288 FileReader<T>::FileReader(std::vector<string> extensions): extensions(extensions) {
289 getFileReaders().push_back(this);
290 std::vector<string> nextExtension = extensions;
291 for(unsigned int i = 0; i < nextExtension.size(); ++i) {
292 vector<FileReader<T> const* >* curPossibleReaders = (getFileReadersMap())[nextExtension[i]];
293 if (curPossibleReaders == NULL) {
294 (getFileReadersMap())[nextExtension[i]] = (curPossibleReaders = new vector<FileReader<T> const *>());
295 }
296 curPossibleReaders->push_back(this);
297 }
298 }
299
300 template <typename T>
301 void FileReader<T>::cleanupExtensions() {
302 std::vector<string> nextExtension = extensions;
303 for(unsigned int i = 0; i < nextExtension.size(); ++i) {
304 vector<FileReader<T> const* >* curPossibleReaders = (getFileReadersMap())[nextExtension[i]];
305 if (curPossibleReaders != NULL) {
306 delete curPossibleReaders;
307 (getFileReadersMap())[nextExtension[i]] = NULL;
308 }
309 }
310 }
311
312 /**Gives a quick estimation of whether the specified file
313 * can be read or not depending on the filename*/
314 template <typename T>
315 bool FileReader<T>::canRead(const string& filepath) const {
316 const string& realExtension = extractExtension(filepath);
317 //const string* haveExtension = extensions;
318 std::vector<string> haveExtension = extensions;
319 //while (*haveExtension != "") {
320 for(unsigned int i = 0; i < haveExtension.size(); ++i) {
321 //if (realExtension == *haveExtension) {
322 if (realExtension == haveExtension[i]) {
323 return true;
324 }
325 //++haveExtension;
326 }
327 return false;
328 }
329
330 /**Gives a better estimation of whether the specified file
331 * can be read or not depending on the file content*/
332 template <typename T>
333 bool FileReader<T>::canRead(ifstream& file) const {
334 try {
335 T* wouldRead = read(file,"unknown file");
336 bool ret = (wouldRead != NULL);
337 delete wouldRead;
338 return ret;
339 }
340 #if defined(WIN32)
341 catch (megaglest_runtime_error) {
342 #else
343 catch (megaglest_runtime_error &ex) {
344 #endif
345 throw;
346 }
347 catch (...) {
348 return false;
349 }
350 }
351
352 /**Reads a file
353 * This method tries to read the file with the specified filepath
354 * If it fails, either <code>null</code> is returned or an exception
355 * is thrown
356 */
357 template <typename T>
358 T* FileReader<T>::read(const string& filepath) const {
359 #if defined(WIN32) && !defined(__MINGW32__)
360 FILE *fp = _wfopen(utf8_decode(filepath).c_str(), L"rb");
361 ifstream file(fp);
362 #else
363 ifstream file(filepath.c_str(), ios::in | ios::binary);
364 #endif
365 if (!file.is_open()) {
366 throw megaglest_runtime_error("[#3] Could not open file " + filepath);
367 }
368 T* ret = read(file,filepath);
369 file.close();
370 #if defined(WIN32) && !defined(__MINGW32__)
371 if(fp) fclose(fp);
372 #endif
373
374 return ret;
375 }
376
377 /**Reads a file
378 * This method tries to read the file with the specified filepath
379 * If it fails, either <code>null</code> is returned or an exception
380 * is thrown
381 */
382 template <typename T>
383 T* FileReader<T>::read(const string& filepath, T* object) const {
384 #if defined(WIN32) && !defined(__MINGW32__)
385 FILE *fp = _wfopen(utf8_decode(filepath).c_str(), L"rb");
386 ifstream file(fp);
387 #else
388 ifstream file(filepath.c_str(), ios::in | ios::binary);
389 #endif
390 if (!file.is_open()) {
391 throw megaglest_runtime_error("[#4] Could not open file " + filepath);
392 }
393 T* ret = read(file,filepath,object);
394 file.close();
395 #if defined(WIN32) && !defined(__MINGW32__)
396 if(fp) fclose(fp);
397 #endif
398
399 return ret;
400 }
401
402
403 } //end namespace
404
405 #endif
406