Mercurial > repos > IBBoard.WarFoundry.API
diff api/Factories/Xml/WarFoundryXmlFactory.cs @ 0:520818033bb6
Initial commit of WarFoundry code
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Fri, 19 Dec 2008 15:57:51 +0000 |
parents | |
children | 150a5669cd7b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/api/Factories/Xml/WarFoundryXmlFactory.cs Fri Dec 19 15:57:51 2008 +0000 @@ -0,0 +1,696 @@ +// WarFoundryXmlFactory.cs +// +// Copyright (C) 2007 IBBoard +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 of the License as published by the Free +// Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// + +using System; +using System.IO; +using System.Xml; +using System.Xml.Schema; +using System.Collections.Generic; +using System.Text; +using IBBoard; +using IBBoard.IO; +using IBBoard.Lang; +using IBBoard.Logging; +using IBBoard.Xml; +using IBBoard.WarFoundry.API.Requirements; +using IBBoard.WarFoundry.API.Objects; +using ICSharpCode.SharpZipLib.Zip; + +namespace IBBoard.WarFoundry.API.Factories.Xml +{ + /// <summary> + /// Summary description for WarFoundryXmlFactory. + /// </summary> + public class WarFoundryXmlFactory : AbstractNativeWarFoundryFactory + { + private Dictionary<IWarFoundryObject, XmlDocument> extraData = new Dictionary<IWarFoundryObject, XmlDocument>(); + private XmlResolver xmlResolver; + + public static AbstractNativeWarFoundryFactory CreateFactory() + { + return new WarFoundryXmlFactory(); + } + + protected WarFoundryXmlFactory() : base() + { + xmlResolver = new IBBXmlResolver(Constants.ExecutablePath); + } + + protected override bool CheckCanFindArmyFileContent(ZipFile file) + { + return file.FindEntry("data.armyx", true) > -1; + } + + protected override bool CheckCanFindSystemFileContent(ZipFile file) + { + return file.FindEntry("data.systemx", true) > -1; + } + + protected override bool CheckCanFindRaceFileContent(ZipFile file) + { + return file.FindEntry("data.racex", true) > -1; + } + + protected XmlElement GetRootElementFromStream(Stream stream, WarFoundryXmlElementName elementName) + { + XmlDocument doc = CreateXmlDocumentFromStream(stream); + XmlElement elem = (XmlElement)doc.LastChild; + + if (!elem.Name.Equals(elementName.Value)) + { + throw new InvalidFileException(String.Format("Root element of XML was not valid. Expected {0} but got {1}", elementName.Value, elem.Name)); + } + + return elem; + } + + protected override Stream GetArmyDataStream(ZipFile file) + { + return file.GetInputStream(file.FindEntry("data.armyx", true)); + } + + protected override Army CreateArmyFromStream (ZipFile file, Stream dataStream) + { + XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.ARMY_ELEMENT); + return CreateArmyFromElement(file, elem); + } + + private Army CreateArmyFromElement(ZipFile file, XmlElement elem) + { + string name = elem.GetAttribute("name"); + string systemID = elem.GetAttribute("gameSystem"); + GameSystem system = WarFoundryLoader.GetDefault().GetGameSystem(systemID); + string raceID = elem.GetAttribute("race"); + Race race = WarFoundryLoader.GetDefault().GetRace(system, raceID); + string pointsString = elem.GetAttribute("maxPoints"); + int points = 0; + + try + { + points = int.Parse(pointsString); + } + catch(FormatException) + { + throw new FormatException("Attribute 'maxPoints' of army '"+name+"' was not a valid number"); + } + + Army army = new Army(race, name, points, file);//, this); + extraData[army] = elem.OwnerDocument; + return army; + } + + protected override Stream GetGameSystemDataStream (ZipFile file) + { + return file.GetInputStream(file.FindEntry("data.systemx", true)); + } + + protected override GameSystem CreateGameSystemFromStream (ZipFile file, Stream dataStream) + { + XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.SYSTEM_ELEMENT); + LogNotifier.Debug(GetType(), "Create GameSystem"); + return CreateSystemFromElement(file, elem); + } + + private GameSystem CreateSystemFromElement(ZipFile file, XmlElement elem) + { + string id = elem.GetAttribute("id"); + string name = elem.GetAttribute("name"); + GameSystem system = new StagedLoadingGameSystem(id, name, this); + //system.SourceZipFile = file.; + extraData[system] = elem.OwnerDocument; + return system; + } + + protected override Stream GetRaceDataStream (ZipFile file) + { + return file.GetInputStream(file.FindEntry("data.racex", true)); + } + + protected override Race CreateRaceFromStream (ZipFile file, Stream dataStream) + { + XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.RACE_ELEMENT); + LogNotifier.Debug(GetType(), "Create Race"); + return CreateRaceFromElement(file, elem); + } + + private Race CreateRaceFromElement(ZipFile file, XmlElement elem) + { + string id = elem.GetAttribute("id"); + string subid = elem.GetAttribute("subid"); + string systemID = elem.GetAttribute("system"); + string name = elem.GetAttribute("name"); + Race race = new StagedLoadingRace(id, subid, name, systemID, this); + //race.SourceZipFile = file; //TODO reference source file + extraData[race] = elem.OwnerDocument; + return race; + } + + /*public WarFoundryObject CreateObjectFromStream(ZipFile file, Stream stream) + { + try + { + WarFoundryObject obj = LoadFileObjectFromElement(file, elem); + + if (obj != null) + { + extraData[obj] = doc; + return obj; + } + else + { + throw new InvalidFileException(String.Format(Translation.GetTranslation("ErrorInvalidXmlFile", "XML file '{0}' was not a loadable XML file. Please ensure it is a valid and supported WarFoundry XML file."), file.Name)); + } + } + catch (XmlSchemaException ex) + { + throw new InvalidFileException(String.Format(Translation.GetTranslation("ErrorInvalidXmlFile", "Failed to parse invalid XML data file in {0}. Error at line {1} position {2}: {3}"), file.Name, ex.LineNumber, ex.LinePosition, ex.Message.Substring(0, ex.Message.IndexOf(".")+1)), ex); + } + catch (InvalidFileException ex) + { + throw new InvalidFileException(String.Format(Translation.GetTranslation("ErrorInvalidNamedXmlFile", "XML data file in '{0}' was not a valid XML file. It should contain three child nodes - XML definition, DocType and root node."), file.Name), ex); + } + } + + private WarFoundryObject LoadFileObjectFromElement(ZipFile file, XmlElement elem) + { + WarFoundryObject obj = null; + + if (elem.Name.Equals(WarFoundryXmlElementName.SYSTEM_ELEMENT.Value)) + { + logger.Debug("Create GameSystem"); + obj = CreateSystemFromElement(file, elem); + } + else if (elem.Name.Equals(WarFoundryXmlElementName.RACE_ELEMENT.Value)) + { + logger.Debug("Create Race"); + obj = CreateRaceFromElement(file, elem); + } + + return obj; + }*/ + + public override void CompleteLoading(IWarFoundryStagedLoadObject obj) + { + LogNotifier.DebugFormat(GetType(), "Complete loading of {0} with ID {1}", obj.GetType().Name, obj.ID); + + if (!obj.IsFullyLoaded) + { + XmlDocument extra = extraData[obj]; + + if (obj is GameSystem) + { + GameSystem system = (GameSystem)obj; + XmlNode elem = extra.LastChild; + + XmlNode catsElem = elem.FirstChild; + Category[] cats; + List<Category> catList = new List<Category>(); + WarFoundryObject tempObj; + + foreach (XmlElement cat in catsElem.ChildNodes) + { + tempObj = CreateObjectFromElement(cat); + + if (tempObj is Category) + { + catList.Add((Category)tempObj); + } + else + { + LogNotifier.WarnFormat(GetType(), "Object for string {0} was not of type Category", cat.OuterXml); + } + } + + cats = catList.ToArray(); + LogNotifier.DebugFormat(GetType(), "Found {0} categories", cats.Length); + + XmlElement statsElem = (XmlElement)catsElem.NextSibling; + Dictionary<string, SystemStats> sysStats = CreateSystemStatsSetFromElement(statsElem); + string defaultStatsID = statsElem.GetAttribute("defaultStats"); + + LogNotifier.DebugFormat(GetType(), "Complete loading of {0}", system.Name); + system.Categories = cats; + system.SystemStats = new SystemStatsSet(sysStats); + system.StandardSystemStatsID = defaultStatsID; + } + else if (obj is Race) + { + Race race = (Race)obj; + XmlNode elem = extra.LastChild; + XmlNode colNode = elem.FirstChild; + + Dictionary<string, UnitType> unitTypes = new Dictionary<string, UnitType>(); + + foreach (XmlElement node in colNode.ChildNodes) + { + UnitType type = CreateUnitTypeFromElement(node, race, race.GameSystem); + unitTypes.Add(type.ID, type); + } + + colNode = colNode.NextSibling; + List<Category> catOverrides = new List<Category>(); + + if (colNode!=null && colNode.Name == WarFoundryXmlElementName.CATEGORIES_ELEMENT.Value) + { + foreach (XmlElement node in colNode.ChildNodes) + { + catOverrides.Add(CreateCategoryFromElement(node)); + } + + colNode = colNode.NextSibling; + } + + Dictionary<string, EquipmentItem> raceEquipment = new Dictionary<string, EquipmentItem>(); + + if (colNode!=null && colNode.Name == WarFoundryXmlElementName.RACE_EQUIPMENT_ITEMS_ELEMENT.Value) + { + foreach (XmlElement node in colNode.ChildNodes) + { + EquipmentItem item = CreateEquipmentItemFromElement(node, race); + raceEquipment.Add(item.ID, item); + } + } + + Dictionary<string, Ability> raceAbilities = new Dictionary<string, Ability>(); + //TODO: Load abilities + + LogNotifier.DebugFormat(GetType(), "Complete loading of {0}", race.Name); + race.Categories = catOverrides.ToArray(); + race.SetUnitTypes(unitTypes); + race.SetEquipmentItems(raceEquipment); + race.SetAbilities(raceAbilities); + } + } + else + { + LogNotifier.Debug(GetType(), "Object is already fully loaded"); + } + } + + protected XmlDocument CreateXmlDocumentFromStream(Stream stream) + { + XmlDocument doc = new XmlDocument(); + XmlReaderSettings settings = new XmlReaderSettings(); + settings.XmlResolver = xmlResolver; + settings.ValidationType = ValidationType.DTD; + settings.ProhibitDtd = false; + settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod); + XmlReader reader = XmlReader.Create(stream, settings); + + try + { + doc.Load(reader); + } + //Don't catch XMLSchemaExceptions - let them get thrown out + finally + { + reader.Close(); + } + + if (doc.ChildNodes.Count!=3) + { + throw new InvalidFileException(Translation.GetTranslation("ErrorInvalidXmlFile", "XML file was not a valid XML file. It should contain three child nodes - XML definition, DocType and root node.")); + } + + return doc; + } + + protected XmlDocument CreateXmlDocumentFromString(string xmlString) + { + XmlReaderSettings settings = new XmlReaderSettings(); + settings.XmlResolver = xmlResolver; + settings.ValidationType = ValidationType.DTD; + settings.ProhibitDtd = false; + settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod); + XmlReader reader = XmlReader.Create(new MemoryStream(Encoding.UTF8.GetBytes(xmlString)), settings); + XmlDocument doc = new XmlDocument(); + + try + { + doc.Load(reader); + } + catch(XmlSchemaException ex) + { + throw new InvalidFileException(Translation.GetTranslation("ErrorInvalidXmlString", "Failed to parse invalid XML string"), ex); + } + finally + { + //We might not need to make sure the memory stream is closed, but do it just in case + reader.Close(); + } + + if (doc.ChildNodes.Count!=3) + { + throw new InvalidFileException(String.Format(Translation.GetTranslation("ErrorInvalidXmlFile", "XML string was not a valid XML file. It should contain three child nodes - XML definition, DocType and root node."))); + } + + return doc; + } + + private WarFoundryObject CreateObjectFromElement(XmlElement elem) + { + WarFoundryObject obj = null; + LogNotifier.DebugFormat(GetType(), "Create object for <{0}>", elem.Name); + + if (elem.Name.Equals(WarFoundryXmlElementName.CATEGORY_ELEMENT.Value)) + { + LogNotifier.Debug(GetType(), "Create Category"); + obj = CreateCategoryFromElement(elem); + } + else + { + LogNotifier.Debug(GetType(), "No match"); + } + + return obj; + } + + private Category CreateCategoryFromElement(XmlElement elem) + { + string id = elem.GetAttribute("id"); + string name = elem.GetAttribute("name"); + int minPc, maxPc, minPts, maxPts, minChoices, maxChoices, baseValue, incValue, incAmount; + + try + { + minPc = int.Parse(elem.GetAttribute("minPercentage")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'minPercentage' of category "+id+" was not a valid number"); + } + + try + { + maxPc = int.Parse(elem.GetAttribute("maxPercentage")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'maxPercentage' of category "+id+" was not a valid number"); + } + + try + { + minPts = int.Parse(elem.GetAttribute("minPoints")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'minPoints' of category "+id+" was not a valid number"); + } + + try + { + maxPts = int.Parse(elem.GetAttribute("maxPoints")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'maxPoints' of category "+id+" was not a valid number"); + } + + try + { + minChoices = int.Parse(elem.GetAttribute("minChoices")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'minChoices' of category "+id+" was not a valid number"); + } + + try + { + maxChoices = int.Parse(elem.GetAttribute("maxChoices")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'maxChoices' of category "+id+" was not a valid number"); + } + + try + { + baseValue = int.Parse(elem.GetAttribute("baseValue")); + + } + catch(FormatException) + { + throw new FormatException("Attribute 'baseValue' of category "+id+" was not a valid number"); + } + + try + { + incValue = int.Parse(elem.GetAttribute("incValue")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'incValue' of category "+id+" was not a valid number"); + } + + try + { + incAmount = int.Parse(elem.GetAttribute("incAmount")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'incAmount' of category "+id+" was not a valid number"); + } + + return new Category(id, name, minPts, maxPts, minPc, maxPc, minChoices, maxChoices, baseValue, incValue, incAmount); + } + + private UnitType CreateUnitTypeFromElement(XmlElement elem, Race parentRace, GameSystem system) + { + string id = elem.GetAttribute("id"); + string name = elem.GetAttribute("typeName"); + string mainCatID = elem.GetAttribute("cat"); + int minNum, maxNum, minSize, maxSize, baseSize;//TODO: Add base size + float points, unitPoints; + Stats stats; + List<UnitRequirement> unitRequirements = new List<UnitRequirement>(); + bool found = false; + List<string> catIDs = new List<string>(); + string catID; + + try + { + minNum = int.Parse(elem.GetAttribute("minNum")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'minNum' of unit "+id+" was not a valid number"); + } + + try + { + maxNum = int.Parse(elem.GetAttribute("maxNum")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'maxNum' of unit "+id+" was not a valid number"); + } + + try + { + minSize = int.Parse(elem.GetAttribute("minSize")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'minSize' of unit "+id+" was not a valid number"); + } + + try + { + maxSize = int.Parse(elem.GetAttribute("maxSize")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'maxSize' of unit "+id+" was not a valid number"); + } + + if (minSize > maxSize && maxSize!=-1) + { + minSize = maxSize; + } + + try + { + points = int.Parse(elem.GetAttribute("points")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'points' of unit "+id+" was not a valid number"); + } + + try + { + unitPoints = int.Parse(elem.GetAttribute("unitPoints")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'trooperPoints' of unit "+id+" was not a valid number"); + } + + XmlNode node = elem.FirstChild; + + foreach(XmlElement cat in node.ChildNodes) + { + catID = cat.GetAttribute("catID"); + catIDs.Add(catID); + + if (catID == mainCatID) + { + found = true; + } + } + + if (!found) + { + throw new InvalidFileException("The main cat "+mainCatID+" was not found in the list of categories for unit "+id); + } + + node = node.NextSibling; + stats = ParseUnitStats((XmlElement)node, system); + //TODO: Add unit requirements + UnitType type = new UnitType(id, name, mainCatID, catIDs.ToArray(), minNum, maxNum, minSize, maxSize, unitPoints, points, stats, unitRequirements.ToArray(), parentRace); + + return type; + } + + private Stats ParseUnitStats(XmlElement elem, GameSystem system) + { + List<Stat> statsList = new List<Stat>(); + String statsID = elem.GetAttribute("statSet"); + SystemStats statsSet; + + if (statsID == "") + { + statsSet = system.StandardSystemStats; + } + else + { + statsSet = system.SystemStats[statsID]; + } + + Stats stats = new Stats(statsSet); + + foreach (XmlElement stat in elem.ChildNodes) + { + String statID = stat.GetAttribute("name"); + StatSlot slot = statsSet[statID]; + + if (slot!=null) + { + statsList.Add(new Stat(slot, stat.InnerText)); + } + else + { + throw new InvalidFileException("The stat "+statID+" was not found in stats set "+statsID); + } + } + + stats.SetStats(statsList); + + return stats; + } + + private Dictionary<string, SystemStats> CreateSystemStatsSetFromElement(XmlElement elem) + { + Dictionary<string, SystemStats> dict = new Dictionary<string,SystemStats>(); + + foreach (XmlElement stats in elem.ChildNodes) + { + SystemStats sysStats = CreateSystemStatsFromElement(stats); + dict.Add(sysStats.ID, sysStats); + } + + return dict; + } + + private SystemStats CreateSystemStatsFromElement(XmlElement elem) + { + List<StatSlot> slots = new List<StatSlot>(); + string id = elem.GetAttribute("id"); + + foreach (XmlElement slot in elem.ChildNodes) + { + StatSlot statSlot = new StatSlot(slot.GetAttribute("name")); + slots.Add(statSlot); + } + + return new SystemStats(id, slots.ToArray()); + } + + private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, Race race) + { + string id = elem.GetAttribute("id"); + string name = elem.GetAttribute("name"); + float cost = 0, min = 0, max = 0; + ArmourType armourType; + + try + { + cost = float.Parse(elem.GetAttribute("cost")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'cost' of equipment item "+id+" was not a valid number"); + } + + try + { + min = float.Parse(elem.GetAttribute("min")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'min' of equipment item "+id+" was not a valid number"); + } + + try + { + max = float.Parse(elem.GetAttribute("max")); + } + catch(FormatException) + { + throw new FormatException("Attribute 'max' of equipment item "+id+" was not a valid number"); + } + + try + { + armourType = (ArmourType)Enum.Parse(typeof(ArmourType), elem.GetAttribute("armourType")); + } + catch(FormatException) + { + throw new InvalidFileException("Attribute 'armourType' of equipment "+id+" was not a valid value from the enumeration"); + } + + if (elem.ChildNodes.Count>0) + { + //It has stats! + //TODO: Parse equipment stats + } + + return new EquipmentItem(id, name, cost, min, max, armourType, race); + } + + private void ValidationEventMethod(object sender, ValidationEventArgs e) + { + //TODO: Fire a validation failure event + LogNotifier.WarnFormat(GetType(), "Validation Error: {0}", e.Message); + } + } +} \ No newline at end of file