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