changeset 22:ea058f9ea9d4

Closes #15 - Migrate to schema for translations * Add XSD and remove DTD for translation document format * Copy XSD to output on compile * Make Translations validate against a schema and handle new exceptions caused by Schema use Also: * Tweak resolver so that it handles paths starting "dtds/" as a special case instead of just "dtds"
author IBBoard <dev@ibboard.co.uk>
date Sat, 07 Mar 2009 15:52:27 +0000
parents c8d74202182a
children fb4fdab841db
files IBBoard.csproj Lang/Translation.cs Xml/IBBXmlResolver.cs dtds/translation.dtd dtds/translation.xsd
diffstat 5 files changed, 69 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/IBBoard.csproj	Thu Mar 05 20:34:31 2009 +0000
+++ b/IBBoard.csproj	Sat Mar 07 15:52:27 2009 +0000
@@ -142,6 +142,8 @@
   <ItemGroup>
     <None Include="COPYING.GPL" />
     <None Include="COPYING.LGPL" />
-    <None Include="dtds\translation.dtd" />
+    <None Include="dtds\translation.xsd">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
   </ItemGroup>
 </Project>
\ No newline at end of file
--- a/Lang/Translation.cs	Thu Mar 05 20:34:31 2009 +0000
+++ b/Lang/Translation.cs	Sat Mar 07 15:52:27 2009 +0000
@@ -27,7 +27,8 @@
 		private static string lang = "";
 		private static DirectoryInfo translationDir;
 		private static Dictionary<string, string> translationsLocal;
-		private static Dictionary<string, string> translationsDefault;
+		private static Dictionary<string, string> translationsDefault;
+		private static XmlReaderSettings settings;
 
 		/// <summary>
 		/// Initialises the translations for the language specified and the default translations so that the Translation class can be used.
@@ -67,18 +68,17 @@
 		
 		private static XmlDocument LoadTranslationDocument(FileInfo file)
 		{
-			XmlDocument doc = new XmlDocument();
-			XmlReaderSettings settings = new XmlReaderSettings();
-			settings.XmlResolver = new IBBXmlResolver(translationDir.Parent.FullName);
-			settings.ValidationType = ValidationType.DTD;
-			settings.ProhibitDtd = false;
-			settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod);
-			XmlReader valReader = XmlReader.Create(file.FullName, settings);
+			XmlDocument doc = new XmlDocument();			
+			XmlReader valReader = XmlReader.Create(file.FullName, GetReaderSettings());
 			
 			try
 			{
 				doc.Load(valReader);
 			}
+			catch (DirectoryNotFoundException ex)
+			{
+				throw new TranslationLoadException("Problem validating schema for translation: " + ex.Message, ex);
+			}
 			catch (XmlSchemaException ex)
 			{
 				throw new TranslationLoadException("Problem validating schema for translation: " + ex.Message, ex);
@@ -95,6 +95,44 @@
 			return doc;
 		}
 		
+		/// <summary>
+		/// Lazy-getter for XML reader settings. May throw a <see cref="TranslationLoadException"/> 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>
+		private static XmlReaderSettings GetReaderSettings()
+		{
+			if (settings == null)
+			{
+				try
+				{
+					settings = new XmlReaderSettings();
+					settings.XmlResolver = new IBBXmlResolver(translationDir.Parent.FullName);
+					settings.ValidationType = ValidationType.Schema;
+					settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings | XmlSchemaValidationFlags.ProcessSchemaLocation;
+					settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod);
+					XmlSchemaSet cache = new XmlSchemaSet();
+					cache.Add("http://ibboard.co.uk/translation", translationDir.Parent.FullName + "/dtds/translation.xsd");
+					settings.Schemas.Add(cache);
+				}
+				catch (DirectoryNotFoundException ex)
+				{
+					throw new TranslationLoadException("Problem validating schema for translation: " + ex.Message, ex);
+				}
+				catch (XmlSchemaException ex)
+				{
+					throw new TranslationLoadException("Problem validating schema for translation: " + ex.Message, ex);
+				}
+				catch (XmlException ex)
+				{
+					throw new TranslationLoadException("Problem reading data for schema: " + ex.Message, ex);
+				}
+			}
+			
+			return settings;
+		}
+		
 		private static FileInfo GetTranslationFile(string language)
 		{
 			FileInfo file = new FileInfo(translationDir.FullName + Constants.DirectoryString + language + ".translation");
@@ -147,8 +185,7 @@
 		
 		private static void ValidationEventMethod(object sender, ValidationEventArgs e)
 		{
-			//TODO: Fire a validation failure event
-    		LogNotifier.Error(typeof(Translation), "Validation Error", e.Exception);
+			throw new TranslationLoadException("Problem validating schema for translation: " + e.Exception.Message, e.Exception);
 		}
 
 		/// <summary>
--- a/Xml/IBBXmlResolver.cs	Thu Mar 05 20:34:31 2009 +0000
+++ b/Xml/IBBXmlResolver.cs	Sat Mar 07 15:52:27 2009 +0000
@@ -21,7 +21,7 @@
 
 		public override Uri ResolveUri(Uri baseUri, string relativeUri)
 		{
-			if (relativeUri.StartsWith("dtds"))
+			if (relativeUri.StartsWith("dtds/"))
 			{
 				//Uri uri = base.ResolveUri(baseUri, relativeUri);
 				return new Uri(Uri.UriSchemeFile + "://" + baseFilePath + Constants.DirectoryString + relativeUri);
--- a/dtds/translation.dtd	Thu Mar 05 20:34:31 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-<!ELEMENT translations (translation*)>
-<!ELEMENT translation (#PCDATA)> <!-- it's a damned ugly hack, but C# won't take "#CDATA" so use #PCDATA and always treat it as CDATA -->
-<!ATTLIST translation id ID #REQUIRED>
-<!ATTLIST translations lang CDATA #REQUIRED>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dtds/translation.xsd	Sat Mar 07 15:52:27 2009 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://ibboard.co.uk/translation" xmlns="http://ibboard.co.uk/translation" elementFormDefault="qualified">
+<xs:complexType name="translationtype">
+    <xs:simpleContent>
+      <xs:extension base="xs:string">
+        <xs:attribute name="id" type="xs:ID" />
+      </xs:extension>
+    </xs:simpleContent>
+</xs:complexType>
+<xs:element name="translations">
+  <xs:complexType>
+    <xs:sequence>
+      <xs:element name="translation" maxOccurs="unbounded" type="translationtype"/>
+    </xs:sequence>
+    <xs:attribute name="lang" type="xs:string" use="required"/>
+  </xs:complexType>
+</xs:element>
+</xs:schema>
\ No newline at end of file