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