view api/Factories/Xml/WarFoundryXmlFactoryUtils.cs @ 150:b36cc4af435b

Re #176: Bug when saving recently edited army * Try to make sure that we clear up more of our open streams Bug seems to be state related in some way since I can only trigger it when loading the file as the first action, but doesn't seem to be related to file loading of other data files since a diagnostic hard-coded "LoadFiles()" call in the FrmMain constructor doesn't change the behaviour
author IBBoard <dev@ibboard.co.uk>
date Sat, 26 Sep 2009 10:43:28 +0000
parents 0b32cc40d82f
children c1caf467dd40
line wrap: on
line source

//  This file (WarFoundryXmlFactoryUtils.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 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 IBBoard.Logging;
using IBBoard.WarFoundry.API.Objects;

namespace IBBoard.WarFoundry.API.Factories.Xml
{
	/// <summary>
	/// A collection of useful utility methods for loading WarFoundry data from XML files
	/// </summary>
	public class WarFoundryXmlFactoryUtils
	{
		public static readonly string NS_BASE = "http://ibboard.co.uk/warfoundry/";
		private static XmlReaderSettings settings;
		private static XmlNamespaceManager nsManager;
		
		public static XmlNodeList SelectNodes(XmlNode element, string xpathQuery)
		{
			return element.SelectNodes(xpathQuery, GetNamespaceManager());
		}
		
		public static XmlNode SelectSingleNode(XmlNode element, string xpathQuery)
		{
			return element.SelectSingleNode(xpathQuery, GetNamespaceManager());
		}
		
		public static XmlElement SelectSingleElement(XmlNode element, string xpathQuery)
		{
			XmlNode node = SelectSingleNode(element, xpathQuery);
			return (node is XmlElement) ? (XmlElement) node : null;
		}
				
		public static XmlNamespaceManager GetNamespaceManager()
		{
			if (nsManager == null)
			{
				nsManager = new XmlNamespaceManager(new NameTable());
				nsManager.AddNamespace("core", NS_BASE + "core");
				nsManager.AddNamespace("cat", NS_BASE + "cats");
				nsManager.AddNamespace("race", NS_BASE + "race");
				nsManager.AddNamespace("system", NS_BASE + "system");
				nsManager.AddNamespace("army", NS_BASE + "army");
			}
			
			return nsManager;
		}
		
		/// <summary>
		/// Lazy-getter for XML reader settings. May throw a <see cref="InvalidDataException"/> if there is a problem with the translation schema.
		/// </summary>
		/// <returns>
		/// A <see cref="XmlReaderSettings"/> with the default values for validating the translation document against the translation schema
		/// </returns>
		public static XmlReaderSettings GetReaderSettings()
		{
			if (settings == null)
			{
				settings = new XmlReaderSettings();
				settings.ValidationType = ValidationType.Schema;
				settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;
				settings.ProhibitDtd = true;
				settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod);
				XmlSchemaSet cache = new XmlSchemaSet();
				string path =  IBBoard.Constants.ExecutablePath + "/dtds/";
				AddSchemaToCache(cache, NS_BASE + "core", path + "warfoundry-core.xsd");
				AddSchemaToCache(cache, NS_BASE + "cats", path + "warfoundry-cats.xsd");
				AddSchemaToCache(cache, NS_BASE + "race", path + "race.xsd");
				AddSchemaToCache(cache, NS_BASE + "system", path + "system.xsd");
				AddSchemaToCache(cache, NS_BASE + "army", path + "army.xsd");
				settings.Schemas.Add(cache);
			}
			
			return settings;
		}
		
		private static void ValidationEventMethod(object sender, ValidationEventArgs e)
		{
			throw new InvalidDataException("Problem validating against schema for WarFoundry data: " + e.Message, e.Exception);
		}
		
		private static void AddSchemaToCache(XmlSchemaSet cache, string xmlNamespace, string schemaLocation)
		{
			try
			{
				cache.Add(xmlNamespace, schemaLocation);
			}
			catch (IOException ex)
			{
				LogNotifier.Warn(typeof(WarFoundryXmlFactoryUtils), "Problem reading schema: " + ex.Message, ex);
			}
			catch (XmlSchemaException ex)
			{
				LogNotifier.Warn(typeof(WarFoundryXmlFactoryUtils), "Problem validating schema for WarFoundry data: " + ex.Message, ex);
			}
			catch (XmlException ex)
			{
				LogNotifier.Warn(typeof(WarFoundryXmlFactoryUtils), "Problem reading data for schema: " + ex.Message, ex);
			}
		}
		
		public static XmlDocument CreateXmlDocumentFromStream(Stream stream)
		{
			XmlDocument doc = new XmlDocument();
			XmlReader reader = XmlReader.Create(stream, GetReaderSettings());
			
			try
			{
				doc.Load(reader);
			}
			//Don't catch XMLSchemaExceptions - let them get thrown out
			finally
			{
				reader.Close();
			}

			return doc;
		}
		
		public static bool CanCompleteLoading(IWarFoundryStagedLoadObject obj)
		{
			bool canLoad = true;			
			
			if (obj.IsFullyLoaded)
			{
				LogNotifier.DebugFormat(typeof(WarFoundryXmlFactoryUtils), "Object of type {0} with ID {1} is already fully loaded", obj.GetType().Name, obj.ID);
				canLoad = false;
			}
			else if (obj.IsLoading)
			{
				LogNotifier.WarnFormat(typeof(WarFoundryXmlFactoryUtils), "Object of type {0} with ID {1} is already being loaded", obj.GetType().Name, obj.ID);
				canLoad = false;
			}
			
			return canLoad;
		}
	}
}