Mercurial > repos > IBDev-IBBoard.WarFoundry.API
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 |