comparison api/WarFoundryLoader.cs @ 14:0770e5cbba7c

Closes #21 - File loading in order * Reworked LoadFiles to smaller methods for readability (also re #10) and structure * Now determine expected load return before loading then load all "expected GameSystem" before "expected Race" * Make "can load as race/game system/army" methods public in interface Re #22 - Get errored file loading * Created FileLoadFailure class and made LoadFiles return a list of them Also * Some code cleanup * Change to DictionaryUtils calls
author IBBoard <dev@ibboard.co.uk>
date Sun, 25 Jan 2009 14:03:20 +0000
parents 613bc5eaac59
children 306558904c2a
comparison
equal deleted inserted replaced
13:ad8eaed12e66 14:0770e5cbba7c
150 } 150 }
151 151
152 /// <summary> 152 /// <summary>
153 /// Loads all of the data files in the registered directories. 153 /// Loads all of the data files in the registered directories.
154 /// </summary> 154 /// </summary>
155 public void LoadFiles() 155 /// <returns>
156 /// A <see cref="Dictionary"/> of files that failed to load mapped against the message that their failure returned
157 /// </returns>
158 public List<FileLoadFailure> LoadFiles()
156 { 159 {
157 LogNotifier.Debug(GetType(), "Load files"); 160 LogNotifier.Debug(GetType(), "Load files");
158 PrepareForFileLoad(); 161 PrepareForFileLoad();
159 162 Dictionary<FileInfo, IWarFoundryFactory> loadableRaces = new Dictionary<FileInfo, IWarFoundryFactory>();
160 foreach (DirectoryInfo directory in directories) 163 Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems = new Dictionary<FileInfo, IWarFoundryFactory>();
161 { 164 List<FileLoadFailure> failedLoads = FillLoadableFiles(loadableRaces, loadableGameSystems);
162 if (directory.Exists) 165 failedLoads.AddRange(LoadGameSystems(loadableGameSystems));
163 { 166 failedLoads.AddRange(LoadRaces(loadableRaces));
164 LogNotifier.Debug(GetType(), "Load from "+directory.FullName); 167
165 168 LogNotifier.Debug(GetType(), failedLoads.Count + " failed file loads");
166 foreach (FileInfo file in directory.GetFiles()) 169
167 { 170 return failedLoads;
168 LoadFile(file);
169 }
170 }
171 else
172 {
173 LogNotifier.WarnFormat(GetType(), "Load for {0} failed because directory didn't exist", directory.FullName);
174 }
175 }
176
177 ICollection<Race> races = new List<Race>();
178
179 foreach (SimpleSet<IWarFoundryObject> objs in loadedObjects.Values)
180 {
181 foreach (IWarFoundryObject obj in objs)
182 {
183 if (obj is Race)
184 {
185 races.Add((Race)obj);
186 }
187 else if (obj is GameSystem)
188 {
189 StoreGameSystem((GameSystem)obj);
190 }
191 }
192 }
193
194 foreach (Race race in races)
195 {
196 StoreRace(race);
197 }
198 } 171 }
199 172
200 protected void PrepareForFileLoad() 173 protected void PrepareForFileLoad()
201 { 174 {
202 //Just set up blank dictionaries for now - may try different and more complex handling in future 175 //Just set up blank dictionaries for now - may try different and more complex handling in future
203 systemsTable = new Dictionary<string,GameSystem>(); 176 systemsTable = new Dictionary<string,GameSystem>();
204 racesTable = new Dictionary<string,Dictionary<string,Dictionary<string,Race>>>(); 177 racesTable = new Dictionary<string,Dictionary<string,Dictionary<string,Race>>>();
205 } 178 }
179
180 private List<FileLoadFailure> FillLoadableFiles(Dictionary<FileInfo, IWarFoundryFactory> loadableRaces, Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems)
181 {
182 List<FileLoadFailure> fails = new List<FileLoadFailure>();
183
184 foreach (DirectoryInfo directory in directories)
185 {
186 if (directory.Exists)
187 {
188 List<FileLoadFailure> directoryFails = FillLoadableFilesForDirectory(loadableRaces, loadableGameSystems, directory);
189 fails.AddRange(directoryFails);
190 }
191 else
192 {
193 LogNotifier.WarnFormat(GetType(), "Load for {0} failed because directory didn't exist", directory.FullName);
194 }
195 }
196
197 return fails;
198 }
199
200 private List<FileLoadFailure> FillLoadableFilesForDirectory(Dictionary<FileInfo, IWarFoundryFactory> loadableRaces, Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems, DirectoryInfo directory)
201 {
202 List<FileLoadFailure> fails = new List<FileLoadFailure>();
203 LogNotifier.Debug(GetType(), "Load from "+directory.FullName);
204
205 foreach (FileInfo file in directory.GetFiles())
206 {
207 IWarFoundryFactory factory = GetGameSystemRaceLoadingFactoryForFile(file);
208
209 if (factory != null)
210 {
211 loadableGameSystems.Add(file, factory);
212 }
213 else
214 {
215 factory = GetRaceLoadingFactoryForFile(file);
216
217 if (factory!=null)
218 {
219 loadableRaces.Add(file, factory);
220 }
221 else
222 {
223 fails.Add(new FileLoadFailure(file, "FileNotHandled", "File not handled as a Race or Game System definition: {0}"));
224 }
225 }
226 }
227
228 return fails;
229 }
230
231 private IWarFoundryFactory GetGameSystemRaceLoadingFactoryForFile(FileInfo file)
232 {
233 IWarFoundryFactory loadingFactory = null;
234
235 foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
236 {
237 if (factory.CanHandleFileAsGameSystem(file))
238 {
239 loadingFactory = factory;
240 break;
241 }
242 }
243
244 if (loadingFactory == null)
245 {
246 foreach (INativeWarFoundryFactory factory in factories)
247 {
248 if (factory.CanHandleFileAsGameSystem(file))
249 {
250 loadingFactory = factory;
251 break;
252 }
253 }
254 }
255
256 return loadingFactory;
257 }
258
259 private IWarFoundryFactory GetRaceLoadingFactoryForFile(FileInfo file)
260 {
261 IWarFoundryFactory loadingFactory = null;
262
263 foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
264 {
265 if (factory.CanHandleFileAsRace(file))
266 {
267 loadingFactory = factory;
268 break;
269 }
270 }
271
272 if (loadingFactory == null)
273 {
274 foreach (INativeWarFoundryFactory factory in factories)
275 {
276 if (factory.CanHandleFileAsRace(file))
277 {
278 loadingFactory = factory;
279 break;
280 }
281 }
282 }
283
284 return loadingFactory;
285 }
286
287 private List<FileLoadFailure> LoadGameSystems(Dictionary<FileInfo, IWarFoundryFactory> gameSystemFiles)
288 {
289 List<FileLoadFailure> fails = new List<FileLoadFailure>();
290
291
292 foreach (FileInfo file in gameSystemFiles.Keys)
293 {
294 try
295 {
296 bool loaded = LoadObject(file, gameSystemFiles[file]);
297
298 if (!loaded)
299 {
300 fails.Add(new FileLoadFailure(file, "FileLoadFailed", "Failed to load {0} as Race using {1}"));
301 }
302 }
303 catch (Exception ex)
304 {
305 fails.Add(new FileLoadFailure(file, ex.Message));
306 }
307 }
308
309 return fails;
310 }
311
312 private List<FileLoadFailure> LoadRaces(Dictionary<FileInfo, IWarFoundryFactory> raceFiles)
313 {
314 List<FileLoadFailure> fails = new List<FileLoadFailure>();
315
316 foreach (FileInfo file in raceFiles.Keys)
317 {
318 try
319 {
320 bool loaded = LoadObject(file, raceFiles[file]);
321
322 if (!loaded)
323 {
324 fails.Add(new FileLoadFailure(file, "FileLoadFailed", "Failed to load {0} as Race using {1}"));
325 }
326 }
327 catch (Exception ex)
328 {
329 fails.Add(new FileLoadFailure(file, ex.Message));
330 }
331 }
332
333 return fails;
334 }
335
336 private bool LoadObject(FileInfo file, IWarFoundryFactory factory)
337 {
338 bool loaded = false;
339
340 LogNotifier.DebugFormat(GetType(), "Loading {0} using {1}", file.FullName, factory.GetType().Name);
341 ICollection<IWarFoundryObject> objects = factory.CreateObjectsFromFile(file);
342
343 if (objects.Count > 0)
344 {
345 AddLoadedObjects(objects, factory);
346 loaded = true;
347 }
348
349 return loaded;
350 }
351
206 352
207 /// <summary> 353 /// <summary>
208 /// Loads a single file through the registered WarFoundryFactories, if a factory exists that supports the file format. 354 /// Loads a single file through the registered WarFoundryFactories, if a factory exists that supports the file format.
209 /// </summary> 355 /// </summary>
210 /// <param name="file"> 356 /// <param name="file">
211 /// A <see cref="FileInfo"/> for the file to attempt to load 357 /// A <see cref="FileInfo"/> for the file to attempt to load
212 /// </param> 358 /// </param>
213 protected void LoadFile(FileInfo file) 359 /// <returns>
214 { 360 /// An ICollection of IWarFoundryObjects loaded from <code>file</code>
215 bool handled = false; 361 /// </returns>
216 362 public ICollection<IWarFoundryObject> LoadFile(FileInfo file)
217 if (!handled) 363 {
218 { 364 ICollection<IWarFoundryObject> objs = null;
219 ICollection<IWarFoundryObject> objs = null; 365 IWarFoundryFactory loadFactory = null;
220 IWarFoundryFactory loadFactory = null; 366
367 if (nonNativeFactories.Count > 0)
368 {
369 LogNotifier.Debug(GetType(), "Attempting to load "+file.FullName+" as a non-native file");
221 370
222 if (nonNativeFactories.Count > 0) 371 foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
223 { 372 {
224 LogNotifier.Debug(GetType(), "Attempting to load "+file.FullName+" as a non-native file"); 373 bool canLoad = factory.CanHandleFileFormat(file);
374 LogNotifier.Debug(GetType(), "Load using "+factory.GetType().FullName+"? " + (canLoad ? "yes" : "no"));
225 375
226 foreach (INonNativeWarFoundryFactory factory in nonNativeFactories) 376 if (canLoad)
227 { 377 {
228 LogNotifier.Debug(GetType(), "Load using "+factory.GetType().AssemblyQualifiedName+"? " + (factory.CanHandleFileFormat(file) ? "yes" : "no")); 378 objs = factory.CreateObjectsFromFile(file);
229 379
230 if (factory.CanHandleFileFormat(file)) 380 if (objs!=null)
231 { 381 {
232 objs = factory.CreateObjectsFromFile(file); 382 loadFactory = factory;
233 383 break;
234 if (objs!=null) 384 }
235 { 385 }
236 loadFactory = factory; 386 }
237 break; 387 }
238 } 388
239 } 389 if (objs == null)
240 } 390 {
241 } 391 LogNotifier.Debug(GetType(), "Attempting to load "+file.FullName+" as native file");
242 392
243 if (objs == null) 393 foreach (INativeWarFoundryFactory factory in factories)
244 { 394 {
245 LogNotifier.Debug(GetType(), "Attempting to load "+file.FullName+" as native file"); 395 if (factory.CanHandleFileFormat(file))
246
247 foreach (INativeWarFoundryFactory factory in factories)
248 { 396 {
249 if (factory.CanHandleFileFormat(file)) 397 objs = factory.CreateObjectsFromFile(file);
398
399 if (objs!=null)
250 { 400 {
251 objs = factory.CreateObjectsFromFile(file); 401 loadFactory = factory;
252 402 break;
253 if (objs!=null)
254 {
255 loadFactory = factory;
256 break;
257 }
258 } 403 }
259 } 404 }
260 } 405 }
261 406 }
262 if (objs!=null) 407
263 { 408 if (objs!=null)
264 AddLoadedObjects(objs, loadFactory); 409 {
265 } 410 AddLoadedObjects(objs, loadFactory);
266 } 411 }
412 else
413 {
414 LogNotifier.Debug(GetType(), "Loading of "+file.FullName+" failed");
415 objs = new List<IWarFoundryObject>();
416 }
417
418 return objs;
267 } 419 }
268 420
269 private void AddLoadedObjects(ICollection<IWarFoundryObject> loadedObjs, IWarFoundryFactory factory) 421 private void AddLoadedObjects(ICollection<IWarFoundryObject> loadedObjs, IWarFoundryFactory factory)
270 { 422 {
271 SimpleSet<IWarFoundryObject> objs; 423 SimpleSet<IWarFoundryObject> objs;
276 objs = new SimpleSet<IWarFoundryObject>(); 428 objs = new SimpleSet<IWarFoundryObject>();
277 loadedObjects.Add(factory, objs); 429 loadedObjects.Add(factory, objs);
278 } 430 }
279 431
280 objs.AddRange(loadedObjs); 432 objs.AddRange(loadedObjs);
281 } 433 StoreObjects(loadedObjs);
282 434 }
283 private void AddLoadedObject(IWarFoundryObject obj, IWarFoundryFactory factory) 435
284 { 436 private void StoreObjects(ICollection<IWarFoundryObject> loadedObjects)
285 SimpleSet<IWarFoundryObject> objs; 437 {
286 loadedObjects.TryGetValue(factory, out objs); 438 foreach (IWarFoundryObject loadedObject in loadedObjects)
287 439 {
288 if (objs == null) 440 if (loadedObject is GameSystem)
289 { 441 {
290 objs = new SimpleSet<IWarFoundryObject>(); 442 StoreGameSystem((GameSystem)loadedObject);
291 loadedObjects.Add(factory, objs); 443 }
292 } 444 else if (loadedObject is Race)
293 445 {
294 objs.Add(obj); 446 StoreRace((Race)loadedObject);
447 }
448 }
295 } 449 }
296 450
297 protected virtual ZipFile MakeZipFile(FileInfo file) 451 protected virtual ZipFile MakeZipFile(FileInfo file)
298 { 452 {
299 return new ZipFile(file.FullName); 453 return new ZipFile(file.FullName);
311 { 465 {
312 systemsTable.Add(sysid, (GameSystem)system); 466 systemsTable.Add(sysid, (GameSystem)system);
313 } 467 }
314 } 468 }
315 469
316
317 /// <summary>
318 /// Stores a loaded <see cref="GameSystem"/> that has been generated outside the core loading structure.
319 ///
320 /// Note: Calls to this function should be made before calls to StoreRace(Race, IWarFoundryFactory).
321 /// </summary>
322 /// <param name="system">
323 /// The <see cref="GameSystem"/> to register as loaded.
324 /// </param>
325 /// <param name="factory">
326 /// The <see cref="IWarFoundryFactory"/> that created it.
327 /// </param>
328 public void StoreGameSystem(GameSystem system, IWarFoundryFactory factory)
329 {
330 AddLoadedObject(system, factory);
331 StoreGameSystem(system);
332 }
333
334 protected void StoreRace(Race race) 470 protected void StoreRace(Race race)
335 { 471 {
336 Dictionary<string, Dictionary<string, Race>> systemRaces; 472 Dictionary<string, Dictionary<string, Race>> systemRaces;
337 473
338 if (race.GameSystem == null) 474 if (race.GameSystem == null)
368 subRaces.Add(race.SubID.ToLower(), race); 504 subRaces.Add(race.SubID.ToLower(), race);
369 } 505 }
370 } 506 }
371 507
372 /// <summary> 508 /// <summary>
373 /// Stores a loaded <see cref="Race"/> that has been generated outside the core loading structure.
374 ///
375 /// Note: Calls to this function should ensure that the relevant <see cref="GameSystem"> has been created first.
376 /// </summary>
377 /// <param name="race">
378 /// The <see cref="Race"/> to register as loaded.
379 /// </param>
380 /// <param name="factory">
381 /// The <see cref="IWarFoundryFactory"/> that created it
382 /// </param>
383 public void StoreRace(Race race, IWarFoundryFactory factory)
384 {
385 AddLoadedObject(race, factory);
386 StoreRace(race);
387 }
388
389 /// <summary>
390 /// Gets all <see cref="GameSystem"/>s that are currently available, determined by those that can be loaded with the current <see cref="IWarFoundryFactory"/>s. 509 /// Gets all <see cref="GameSystem"/>s that are currently available, determined by those that can be loaded with the current <see cref="IWarFoundryFactory"/>s.
391 /// </summary> 510 /// </summary>
392 /// <returns> 511 /// <returns>
393 /// An array of <see cref="GameSystem"/>s that are currently available. 512 /// An array of <see cref="GameSystem"/>s that are currently available.
394 /// </returns> 513 /// </returns>
397 if (systemsTable==null) 516 if (systemsTable==null)
398 { 517 {
399 LoadFiles(); 518 LoadFiles();
400 } 519 }
401 520
402 GameSystem[] systems = new GameSystem[systemsTable.Count]; 521 return DictionaryUtils.ToArray<string, GameSystem>(systemsTable);
403 int iSys = 0;
404
405 foreach (GameSystem sys in systemsTable.Values)
406 {
407 systems[iSys++] = sys;
408 }
409
410 return systems;
411 } 522 }
412 523
413 /// <summary> 524 /// <summary>
414 /// Gets a single <see cref="GameSystem"/> with a given ID. 525 /// Gets a single <see cref="GameSystem"/> with a given ID.
415 /// </summary> 526 /// </summary>