comparison api/Factories/Xml/WarFoundryXmlFactory.cs @ 48:b49372dd8afa

Re #50 - Complete loading of XML files * Load base points * Check category exists when loaded Re #10 - Refactor source code for readability * Rearrange some methods * Move storing "extra data" in to a method
author IBBoard <dev@ibboard.co.uk>
date Sat, 28 Mar 2009 16:37:27 +0000
parents 85f2b9c3609c
children 9d31d063b194
comparison
equal deleted inserted replaced
47:85f2b9c3609c 48:b49372dd8afa
89 string name = elem.GetAttribute("name"); 89 string name = elem.GetAttribute("name");
90 string systemID = elem.GetAttribute("gameSystem"); 90 string systemID = elem.GetAttribute("gameSystem");
91 GameSystem system = WarFoundryLoader.GetDefault().GetGameSystem(systemID); 91 GameSystem system = WarFoundryLoader.GetDefault().GetGameSystem(systemID);
92 string raceID = elem.GetAttribute("race"); 92 string raceID = elem.GetAttribute("race");
93 Race race = WarFoundryLoader.GetDefault().GetRace(system, raceID); 93 Race race = WarFoundryLoader.GetDefault().GetRace(system, raceID);
94 string pointsString = elem.GetAttribute("maxPoints"); 94 int points = GetIntValueFromAttribute(elem, "maxPoints");
95 int points = 0;
96
97 try
98 {
99 points = int.Parse(pointsString);
100 }
101 catch(FormatException)
102 {
103 throw new FormatException("Attribute 'maxPoints' of army '"+name+"' was not a valid number");
104 }
105
106 Army army = new Army(race, name, points, file); 95 Army army = new Army(race, name, points, file);
107 extraData[army] = elem.OwnerDocument; 96 StoreExtraData(army, elem);
108 return army; 97 return army;
98 }
99
100 private void StoreExtraData(WarFoundryStagedLoadingObject wfObject, XmlElement elem)
101 {
102 extraData[wfObject] = elem.OwnerDocument;
103 }
104
105 public XmlDocument GetExtraData(IWarFoundryObject obj)
106 {
107 XmlDocument extra = null;
108 extraData.TryGetValue(obj, out extra);
109 return extra;
109 } 110 }
110 111
111 protected override Stream GetGameSystemDataStream (ZipFile file) 112 protected override Stream GetGameSystemDataStream (ZipFile file)
112 { 113 {
113 return file.GetInputStream(file.FindEntry("data.systemx", true)); 114 return file.GetInputStream(file.FindEntry("data.systemx", true));
123 private GameSystem CreateSystemFromElement(ZipFile file, XmlElement elem) 124 private GameSystem CreateSystemFromElement(ZipFile file, XmlElement elem)
124 { 125 {
125 string id = elem.GetAttribute("id"); 126 string id = elem.GetAttribute("id");
126 string name = elem.GetAttribute("name"); 127 string name = elem.GetAttribute("name");
127 GameSystem system = new GameSystem(id, name, this); 128 GameSystem system = new GameSystem(id, name, this);
128 extraData[system] = elem.OwnerDocument; 129 StoreExtraData(system, elem);
129 return system; 130 return system;
130 } 131 }
131 132
132 protected override Stream GetRaceDataStream (ZipFile file) 133 protected override Stream GetRaceDataStream (ZipFile file)
133 { 134 {
146 string id = elem.GetAttribute("id"); 147 string id = elem.GetAttribute("id");
147 string subid = elem.GetAttribute("subid"); 148 string subid = elem.GetAttribute("subid");
148 string systemID = elem.GetAttribute("system"); 149 string systemID = elem.GetAttribute("system");
149 string name = elem.GetAttribute("name"); 150 string name = elem.GetAttribute("name");
150 Race race = new Race(id, subid, name, systemID, this); 151 Race race = new Race(id, subid, name, systemID, this);
151 extraData[race] = elem.OwnerDocument; 152 StoreExtraData(race, elem);
152 return race; 153 return race;
153 }
154
155 public override void CompleteLoading(IWarFoundryStagedLoadObject obj)
156 {
157 LogNotifier.DebugFormat(GetType(), "Complete loading of {0} with ID {1}", obj.GetType().Name, obj.ID);
158
159 if (obj is GameSystem)
160 {
161 CompleteLoading((GameSystem)obj);
162 }
163 else if (obj is Race)
164 {
165 CompleteLoading((Race)obj);
166 }
167 }
168
169 public XmlNode GetExtraData(IWarFoundryObject obj)
170 {
171 XmlDocument extra = null;
172 extraData.TryGetValue(obj, out extra);
173 XmlNode node = null;
174
175 if (extra !=null)
176 {
177 node = extra.LastChild;
178 }
179
180 return node;
181 }
182
183 public void CompleteLoading(GameSystem system)
184 {
185 if (system.IsFullyLoaded)
186 {
187 LogNotifier.DebugFormat(GetType(), "Object of type GameSystem with ID {0} is already fully loaded", system.ID);
188 return;
189 }
190
191 if (system.IsLoading)
192 {
193 LogNotifier.WarnFormat(GetType(), "Object of type GameSystem with ID {0} is already being loaded", system.ID);
194 return;
195 }
196
197 system.SetAsLoading();
198
199 XmlNode elem = GetExtraData(system);
200 LoadCategoriesForSystem(system, elem);
201 XmlNodeList nodeList = SelectNodes(elem, "/system:system/system:sysStatsList");
202 XmlNode statsElem = nodeList.Item(0);
203 string defaultStatsID = ((XmlElement)statsElem).GetAttribute("defaultStats");
204 LoadSystemStatsForSystem(system, elem);
205 system.StandardSystemStatsID = defaultStatsID;
206 LogNotifier.DebugFormat(GetType(), "Completed loading of GameSystem with ID {0}", system.ID);
207 LogNotifier.DebugFormat(GetType(), "GameSystem with ID {0} default stats: {1}", system.ID, system.StandardSystemStatsID);
208 system.SetAsFullyLoaded();
209 }
210
211 private void LoadCategoriesForSystem(GameSystem system, XmlNode elem)
212 {
213 WarFoundryObject tempObj;
214
215 foreach (XmlElement cat in SelectNodes(elem, "/system:system/system:categories/cat:cat"))
216 {
217 tempObj = CreateObjectFromElement(cat);
218
219 if (tempObj is Category)
220 {
221 system.AddCategory((Category)tempObj);
222 }
223 else
224 {
225 LogNotifier.WarnFormat(GetType(), "Object for string {0} was not of type Category", cat.OuterXml);
226 }
227 }
228 }
229
230 public void CompleteLoading(Race race)
231 {
232 if (race.IsFullyLoaded)
233 {
234 LogNotifier.DebugFormat(GetType(), "Object of type Race with ID {0} is already fully loaded", race.ID);
235 return;
236 }
237
238 if (race.IsLoading)
239 {
240 LogNotifier.WarnFormat(GetType(), "Object of type Race with ID {0} is already being loaded", race.ID);
241 return;
242 }
243
244 race.SetAsLoading();
245
246 XmlNode elem = GetExtraData(race);
247
248 foreach (XmlElement node in SelectNodes(elem, "/race:race/race:units/race:unit"))
249 {
250 UnitType type = CreateUnitTypeFromElement(node, race, race.GameSystem);
251 race.AddUnitType(type);
252 }
253
254 foreach (XmlElement node in SelectNodes(elem, "/race:race/race:categories/cat:cat"))
255 {
256 race.AddCategory(CreateCategoryFromElement(node));
257 }
258
259 foreach (XmlElement node in SelectNodes(elem, "/race:race/race:equipment/cat:equipmentItem"))
260 {
261 EquipmentItem item = CreateEquipmentItemFromElement(node, race);
262 race.AddEquipmentItem(item);
263 }
264
265 foreach (XmlElement node in SelectNodes(elem, "/race:race/race:abilities/cat:ability"))
266 {
267 Ability ability = CreateAbilityFromElement(node, race);
268 race.AddAbility(ability);
269 }
270
271 race.SetAsFullyLoaded();
272 LogNotifier.DebugFormat(GetType(), "Completed loading of Race with ID {0}", race.ID);
273 } 154 }
274 155
275 protected XmlDocument CreateXmlDocumentFromStream(Stream stream) 156 protected XmlDocument CreateXmlDocumentFromStream(Stream stream)
276 { 157 {
277 XmlDocument doc = new XmlDocument(); 158 XmlDocument doc = new XmlDocument();
288 } 169 }
289 170
290 return doc; 171 return doc;
291 } 172 }
292 173
293 /// <summary> 174 /// <summary>
294 /// Lazy-getter for XML reader settings. May throw a <see cref="InvalidDataException"/> if there is a problem with the translation schema. 175 /// Lazy-getter for XML reader settings. May throw a <see cref="InvalidDataException"/> if there is a problem with the translation schema.
295 /// </summary> 176 /// </summary>
296 /// <returns> 177 /// <returns>
297 /// A <see cref="XmlReaderSettings"/> with the default values for validating the translation document against the translation schema 178 /// A <see cref="XmlReaderSettings"/> with the default values for validating the translation document against the translation schema
298 /// </returns> 179 /// </returns>
317 } 198 }
318 199
319 return settings; 200 return settings;
320 } 201 }
321 202
203 private void ValidationEventMethod(object sender, ValidationEventArgs e)
204 {
205 throw new InvalidDataException("Problem validating against schema for WarFoundry data: " + e.Exception.Message, e.Exception);
206 }
207
322 private void AddSchemaToCache(XmlSchemaSet cache, string xmlNamespace, string schemaLocation) 208 private void AddSchemaToCache(XmlSchemaSet cache, string xmlNamespace, string schemaLocation)
323 { 209 {
324 try 210 try
325 { 211 {
326 cache.Add(xmlNamespace, schemaLocation); 212 cache.Add(xmlNamespace, schemaLocation);
357 private XmlNodeList SelectNodes(XmlNode element, string xpathQuery) 243 private XmlNodeList SelectNodes(XmlNode element, string xpathQuery)
358 { 244 {
359 return element.SelectNodes(xpathQuery, GetNamespaceManager()); 245 return element.SelectNodes(xpathQuery, GetNamespaceManager());
360 } 246 }
361 247
362 private void ValidationEventMethod(object sender, ValidationEventArgs e) 248 private XmlNode SelectSingleNode(XmlNode element, string xpathQuery)
363 { 249 {
364 throw new InvalidDataException("Problem validating against schema for WarFoundry data: " + e.Exception.Message, e.Exception); 250 return element.SelectSingleNode(xpathQuery, GetNamespaceManager());
251 }
252
253 private int GetIntValueFromAttribute(XmlElement elem, string attributeName)
254 {
255 try
256 {
257 return int.Parse(elem.GetAttribute(attributeName));
258 }
259 catch(FormatException)
260 {
261 throw new FormatException(String.Format("Attribute '{0}' of {1} with ID {2} was not a valid number", attributeName, elem.Name, elem.GetAttribute("id")));
262 }
263 }
264
265 private double GetDoubleValueFromAttribute(XmlElement elem, string attributeName)
266 {
267 double doubleVal = double.NaN;
268 string attribValue = elem.GetAttribute(attributeName);
269
270 if (attribValue == "INF")
271 {
272 doubleVal = double.PositiveInfinity;
273 }
274 else
275 {
276 try
277 {
278 return int.Parse(attribValue);
279 }
280 catch(FormatException)
281 {
282 throw new FormatException(String.Format("Attribute '{0}' of {1} with ID {2} was not a valid number", attributeName, elem.Name, elem.GetAttribute("id")));
283 }
284 }
285
286 return doubleVal;
287 }
288
289 public override void CompleteLoading(IWarFoundryStagedLoadObject obj)
290 {
291 LogNotifier.DebugFormat(GetType(), "Complete loading of {0} with ID {1}", obj.GetType().Name, obj.ID);
292
293 if (obj is GameSystem)
294 {
295 CompleteLoading((GameSystem)obj);
296 }
297 else if (obj is Race)
298 {
299 CompleteLoading((Race)obj);
300 }
301 else if (obj is Army)
302 {
303 CompleteLoading((Army) obj);
304 }
305 }
306
307 public void CompleteLoading(GameSystem system)
308 {
309 if (system.IsFullyLoaded)
310 {
311 LogNotifier.DebugFormat(GetType(), "Object of type GameSystem with ID {0} is already fully loaded", system.ID);
312 return;
313 }
314
315 if (system.IsLoading)
316 {
317 LogNotifier.WarnFormat(GetType(), "Object of type GameSystem with ID {0} is already being loaded", system.ID);
318 return;
319 }
320
321 system.SetAsLoading();
322
323 XmlDocument extraData = GetExtraData(system);
324 LoadCategoriesForSystem(system, extraData);
325 XmlElement statsElem = SelectSingleNode(extraData, "/system:system/system:sysStatsList");
326 string defaultStatsID = statsElem.GetAttribute("defaultStats");
327 LoadSystemStatsForSystem(system, extraData);
328 system.StandardSystemStatsID = defaultStatsID;
329 LogNotifier.DebugFormat(GetType(), "Completed loading of GameSystem with ID {0}", system.ID);
330 LogNotifier.DebugFormat(GetType(), "GameSystem with ID {0} default stats: {1}", system.ID, system.StandardSystemStatsID);
331 system.SetAsFullyLoaded();
332 }
333
334 private void LoadCategoriesForSystem(GameSystem system, XmlNode elem)
335 {
336 WarFoundryObject tempObj;
337
338 foreach (XmlElement cat in SelectNodes(elem, "/system:system/system:categories/cat:cat"))
339 {
340 tempObj = CreateObjectFromElement(cat);
341
342 if (tempObj is Category)
343 {
344 system.AddCategory((Category)tempObj);
345 }
346 else
347 {
348 LogNotifier.WarnFormat(GetType(), "Object for string {0} was not of type Category", cat.OuterXml);
349 }
350 }
351 }
352
353 public void CompleteLoading(Race race)
354 {
355 if (race.IsFullyLoaded)
356 {
357 LogNotifier.DebugFormat(GetType(), "Object of type Race with ID {0} is already fully loaded", race.ID);
358 return;
359 }
360
361 if (race.IsLoading)
362 {
363 LogNotifier.WarnFormat(GetType(), "Object of type Race with ID {0} is already being loaded", race.ID);
364 return;
365 }
366
367 race.SetAsLoading();
368
369 XmlDocument extraData = GetExtraData(race);
370
371 foreach (XmlElement node in SelectNodes(extraData, "/race:race/race:units/race:unit"))
372 {
373 UnitType type = CreateUnitTypeFromElement(node, race, race.GameSystem);
374 race.AddUnitType(type);
375 }
376
377 foreach (XmlElement node in SelectNodes(extraData, "/race:race/race:categories/cat:cat"))
378 {
379 race.AddCategory(CreateCategoryFromElement(node));
380 }
381
382 foreach (XmlElement node in SelectNodes(extraData, "/race:race/race:equipment/cat:equipmentItem"))
383 {
384 EquipmentItem item = CreateEquipmentItemFromElement(node, race);
385 race.AddEquipmentItem(item);
386 }
387
388 foreach (XmlElement node in SelectNodes(extraData, "/race:race/race:abilities/cat:ability"))
389 {
390 Ability ability = CreateAbilityFromElement(node, race);
391 race.AddAbility(ability);
392 }
393
394 race.SetAsFullyLoaded();
395 LogNotifier.DebugFormat(GetType(), "Completed loading of Race with ID {0}", race.ID);
365 } 396 }
366 397
367 private WarFoundryObject CreateObjectFromElement(XmlElement elem) 398 private WarFoundryObject CreateObjectFromElement(XmlElement elem)
368 { 399 {
369 WarFoundryObject obj = null; 400 WarFoundryObject obj = null;
391 cat.MinimumPercentage = GetIntValueFromAttribute(elem, "minPercentage"); 422 cat.MinimumPercentage = GetIntValueFromAttribute(elem, "minPercentage");
392 cat.MaximumPoints = GetIntValueFromAttribute(elem, "maxPoints"); 423 cat.MaximumPoints = GetIntValueFromAttribute(elem, "maxPoints");
393 cat.MinimumPoints = GetIntValueFromAttribute(elem, "minPoints"); 424 cat.MinimumPoints = GetIntValueFromAttribute(elem, "minPoints");
394 return cat; 425 return cat;
395 } 426 }
396
397 private int GetIntValueFromAttribute(XmlElement elem, string attributeName)
398 {
399 try
400 {
401 return int.Parse(elem.GetAttribute(attributeName));
402 }
403 catch(FormatException)
404 {
405 throw new FormatException(String.Format("Attribute '{0}' of {1} with ID {2} was not a valid number", attributeName, elem.Name, elem.GetAttribute("id")));
406 }
407 }
408
409 private double GetDoubleValueFromAttribute(XmlElement elem, string attributeName)
410 {
411 double doubleVal = double.NaN;
412 string attribValue = elem.GetAttribute(attributeName);
413
414 if (attribValue == "INF")
415 {
416 doubleVal = double.PositiveInfinity;
417 }
418 else
419 {
420 try
421 {
422 return int.Parse(attribValue);
423 }
424 catch(FormatException)
425 {
426 throw new FormatException(String.Format("Attribute '{0}' of {1} with ID {2} was not a valid number", attributeName, elem.Name, elem.GetAttribute("id")));
427 }
428 }
429
430 return doubleVal;
431 }
432 427
433 private UnitType CreateUnitTypeFromElement(XmlElement elem, Race parentRace, GameSystem system) 428 private UnitType CreateUnitTypeFromElement(XmlElement elem, Race parentRace, GameSystem system)
434 { 429 {
435 string id = elem.GetAttribute("id"); 430 string id = elem.GetAttribute("id");
436 string name = elem.GetAttribute("typeName"); 431 string name = elem.GetAttribute("typeName");
437 UnitType type = new UnitType(id, name, parentRace); 432 UnitType type = new UnitType(id, name, parentRace);
438 type.MaxNumber = GetIntValueFromAttribute(elem, "maxNum"); 433 type.MaxNumber = GetIntValueFromAttribute(elem, "maxNum");
439 type.MinNumber = GetIntValueFromAttribute(elem, "minNum"); 434 type.MinNumber = GetIntValueFromAttribute(elem, "minNum");
440 type.MaxSize = GetIntValueFromAttribute(elem, "maxSize"); 435 type.MaxSize = GetIntValueFromAttribute(elem, "maxSize");
441 type.MinSize = GetIntValueFromAttribute(elem, "minSize"); 436 type.MinSize = GetIntValueFromAttribute(elem, "minSize");
442 //TODO: Add base size 437 type.BaseSize = GetIntValueFromAttribute(elem, "baseSize");
443 type.CostPerTrooper = GetIntValueFromAttribute(elem, "points"); 438 type.CostPerTrooper = GetIntValueFromAttribute(elem, "points");
444 type.BaseUnitCost = GetIntValueFromAttribute(elem, "unitPoints"); 439 type.BaseUnitCost = GetIntValueFromAttribute(elem, "unitPoints");
445 string mainCatID = elem.GetAttribute("cat"); 440 string mainCatID = elem.GetAttribute("cat");
446 type.MainCategory = parentRace.GetCategory(mainCatID); 441 Category cat = parentRace.GetCategory(mainCatID);
447 XmlNodeList nodes = SelectNodes(elem, "/race:race/race:units/race:unit/race:stats"); 442
448 XmlNode node = nodes.Item(0); 443 if (cat == null)
449 type.UnitStats = ParseUnitStats((XmlElement)node, system); 444 {
445 throw new InvalidDataException(String.Format("Attribute 'cat' of UnitType {0} (value: {1}) did not reference a valid category", id, mainCatID));
446 }
447
448 type.MainCategory = cat;
449 XmlElement statsElement = SelectSingleNodes(elem, "/race:race/race:units/race:unit/race:stats");
450 type.UnitStats = ParseUnitStats(statsElement, system);
450 //TODO: Add unit requirements 451 //TODO: Add unit requirements
451 LogNotifier.Debug(GetType(), "Loaded "+type.Name); 452 LogNotifier.Debug(GetType(), "Loaded "+type.Name);
452 return type; 453 return type;
453 } 454 }
454 455