view API/Factories/Xml/WarFoundryXmlFactory.cs @ 370:077e9be48438

Re #346: Add requirement schema support * Pass unit test with a specific case for one requirement - needs extensibility
author IBBoard <dev@ibboard.co.uk>
date Mon, 13 Jun 2011 15:15:04 +0000
parents 1a70ca80ef41
children c3b0b28bad48
line wrap: on
line source

// This file (WarFoundryXmlFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
//
// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.

using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.XPath;
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.Objects;
using ICSharpCode.SharpZipLib.Zip;
using System.Text.RegularExpressions;

namespace IBBoard.WarFoundry.API.Factories.Xml
{
	/// <summary>
	/// The WarFoundryXmlFactory loads WarFoundry classes from the native "XML in a zip" file format. Files are validated using the schema for the file type, so structurally invalid files should be identified at initial load.
	/// </summary>
	public class WarFoundryXmlFactory : AbstractNativeWarFoundryFactory
	{
		private static WarFoundryXmlFactory factory;
		private WarFoundryXmlGameSystemFactory gameSystemFactory;
		private WarFoundryXmlRaceFactory raceFactory;
		private WarFoundryXmlArmyFactory armyFactory;

		public static WarFoundryXmlFactory GetFactory()
		{
			if (factory == null)
			{
				factory = new WarFoundryXmlFactory();
			}
			
			return factory;
		}
		
		private WarFoundryXmlFactory() : base()
		{
			gameSystemFactory = new WarFoundryXmlGameSystemFactory(this);
			raceFactory = new WarFoundryXmlRaceFactory(this);
			armyFactory = new WarFoundryXmlArmyFactory();
		}
		
		public WarFoundryXmlGameSystemFactory GetSystemFactory()
		{
			return gameSystemFactory;
		}
		
		public WarFoundryXmlRaceFactory GetRaceFactory()
		{
			return raceFactory;
		}
		
		public WarFoundryXmlArmyFactory GetArmyFactory()
		{
			return armyFactory;
		}
		
		protected override bool CheckCanFindArmyFileContent(ZipFile file)
		{
			return FindEntries(file, "*.armyx").Count > 0;
		}
		
		protected override bool CheckCanFindSystemFileContent(ZipFile file)
		{
			return FindEntries(file, "*.systemx").Count > 0;
		}
		
		protected override bool CheckCanFindRaceFileContent(ZipFile file)
		{
			return FindEntries(file, "*.racex").Count > 0;
		}
		
		protected override ICollection<ZipEntry> GetArmyZipEntries(ZipFile file)
		{
			return FindEntries(file, "*.armyx");
		}
		
		private ICollection<ZipEntry> FindEntries(ZipFile file, string wildcardPattern)
		{
			Regex re = new Regex("^" + Regex.Escape(wildcardPattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$", RegexOptions.IgnoreCase | RegexOptions.Singleline);
			ICollection<ZipEntry> entries = new List<ZipEntry>();
				
			foreach (ZipEntry entry in file)
			{
				if (re.IsMatch(entry.Name))
				{
					entries.Add(entry);
				}
			}
			
			return entries;
		}
		
		protected override Army CreateArmyFromStream (ZipFile file, Stream dataStream)
		{
			XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.ARMY_ELEMENT);
			return armyFactory.CreateArmyFromElement(file, elem);
		}
		
		private XmlElement GetRootElementFromStream(Stream stream, WarFoundryXmlElementName elementName)
		{
			XmlDocument doc = WarFoundryXmlFactoryUtils.CreateXmlDocumentFromStream(stream);

			XmlElement elem = (XmlElement)doc.LastChild;
			
			if (!elem.LocalName.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 ICollection<ZipEntry> GetGameSystemZipEntries(ZipFile file)
		{
			return FindEntries(file, "*.systemx");
		}
		
		protected override GameSystem CreateGameSystemFromStream (ZipFile file, Stream dataStream)
		{
			XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.SYSTEM_ELEMENT);
			LogNotifier.Debug(GetType(), "Create GameSystem");
			return gameSystemFactory.CreateSystemFromElement(file, elem);
		}
		
		protected override ICollection<ZipEntry> GetRaceZipEntries(ZipFile file)
		{
			return FindEntries(file, "*.racex");
		}
		
		protected override Race CreateRaceFromStream (ZipFile file, Stream dataStream)
		{
			XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.RACE_ELEMENT);
			LogNotifier.Debug(GetType(), "Create Race");
			return raceFactory.CreateRaceFromElement(file, elem);
		}

		protected override void CleanUpFileAsSupportedType(ZipFile typedFile)
		{
			typedFile.Close();
		}

		public override void CompleteLoading(IWarFoundryStagedLoadObject obj)
		{			
			LogNotifier.DebugFormat(GetType(), "Complete loading of {0} with ID {1}", obj.GetType().Name, obj.ID);

			if (obj is GameSystem)
			{
				CompleteLoadingGameSystem((GameSystem) obj);
			}
			else if (obj is Race)
			{
				CompleteLoadingRace((Race) obj);
			}
		}

		private void CompleteLoadingRace(Race race)
		{
			try
			{
				raceFactory.CompleteLoading(race);
			}
			catch (InvalidFileException ex)
			{
				WarFoundryLoader.GetDefault().RemoveRace(race);
				throw;
			}
		}

		private void CompleteLoadingGameSystem(GameSystem system)
		{
			try
			{
				gameSystemFactory.CompleteLoading(system);
			}
			catch (InvalidFileException ex)
			{
				WarFoundryLoader.GetDefault().RemoveGameSystem(system);
				throw;
			}
		}
	}
}