view API/Exporters/WarFoundryHtmlExporter.cs @ 481:81f32062c9fa

Re #419: Remove assumptions of a file-based install * Convert schemas to resources and load them from there * Remove use of "data" folder relative to exe location from default "Hacks" (GTK/WinForms GUIs can re-add it locally)
author IBBoard <dev@ibboard.co.uk>
date Mon, 25 Jun 2012 21:04:02 +0100
parents 6da9db4a9c23
children 55b39514cbf8
line wrap: on
line source

// This file (WarFoundryHtmlExporter.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.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using IBBoard.Lang;
using IBBoard.Xml;
using IBBoard.WarFoundry.API.Objects;
using IBBoard.WarFoundry.API.Util;
using System.Reflection;

namespace IBBoard.WarFoundry.API.Exporters
{
		/// <summary>
		/// Custom exporter that exports an army as a basic HTML file
		/// </summary>
		public class WarFoundryHtmlExporter : IWarFoundryExporter
		{
				private static WarFoundryHtmlExporter exporter;
				private delegate string GetStatCellTextDelegate(Stat stat);
		
				public static WarFoundryHtmlExporter GetDefault()
				{
						if (exporter == null)
						{
								exporter = new WarFoundryHtmlExporter();
						}
			
						return exporter;
				}
		
				private WarFoundryHtmlExporter()
				{
						//Hide constructor
				}
		
				public void ExportArmy(Army army, string path)
				{
						XmlDocument doc = new XmlDocument();
						XmlResourceResolver resolver = new XmlResourceResolver(Assembly.GetExecutingAssembly());
						resolver.AddMapping("-//W3C//ENTITIES Latin 1 for XHTML//EN", "IBBoard.API.schemas.xhtml1-lat1.ent");
						resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent", "IBBoard.API.schemas.xhtml1-lat1.ent");
						resolver.AddMapping("-//W3C//ENTITIES Symbols for XHTML//EN", "IBBoard.API.schemas.xhtml1-symbol.ent");
						resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent", "IBBoard.API.schemas.xhtml1-symbol.ent");
						resolver.AddMapping("-//W3C//ENTITIES Special for XHTML//EN", "IBBoard.API.schemas.xhtml1-special.ent");
						resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent", "IBBoard.API.schemas.xhtml1-special.ent");
						resolver.AddMapping("-//W3C//DTD XHTML 1.0 Strict//EN", "IBBoard.WarFoundry.schemas.xhtml1-strict.dtd");
						resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", "IBBoard.WarFoundry.schemas.xhtml1-strict.dtd");
						doc.XmlResolver = resolver;
						doc.AppendChild(doc.CreateDocumentType("html", "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null));
						XmlElement html = doc.CreateElement("html");
						doc.AppendChild(html);
						XmlElement head = doc.CreateElement("head");
						html.AppendChild(head);
						XmlElement title = doc.CreateElement("title");
						title.InnerXml = army.Name;
						head.AppendChild(title);
						XmlElement metaCharset = doc.CreateElement("meta");
						metaCharset.SetAttribute("http-equiv", "Content-Type");
						metaCharset.SetAttribute("content", "text/html;charset=UTF-8");
						head.AppendChild(metaCharset);
						XmlElement style = doc.CreateElement("style");
						style.InnerText = "table, th, td { border: 1px solid #000; border-spacing: 0; border-collapse: collapse; margin: 0 }\n"
								+ "table table { width: 100%; border-width: 0; margin: -2px }\n"
								+ "table table td { border-width:0 1px }";
						head.AppendChild(style);
						XmlElement body = doc.CreateElement("body");
						html.AppendChild(body);
						XmlElement header = doc.CreateElement("h1");
						header.InnerText = Translation.GetTranslation("armyHtmlOutputBodyHeader", "{0} - {1}pts", army.Name, army.Points);
						body.AppendChild(header);
			
						foreach (XmlElement table in CreateTables(army, doc))
						{
								if (!IsTableOnlyHeader(table))
								{
										body.AppendChild(table);
								}
						}

						File.WriteAllText(path, doc.OuterXml);
				}

				private bool IsTableOnlyHeader(XmlElement table)
				{
						return table.ChildNodes.Count == 1;
				}
		
				private XmlElement[] CreateTables(Army army, XmlDocument doc)
				{
						Dictionary<string, XmlElement> tables = new Dictionary<string, XmlElement>();
			
						foreach (SystemStats statSets in army.GameSystem.SystemStats)
						{
								tables[statSets.ID] = CreateTable(statSets, doc);
						}
			
						foreach (Unit unit in army.GetUnits())
						{
								CreateUnitRow(unit, tables[GetFirstStatType(unit)]);
						}
			
						return DictionaryUtils.ToArray(tables);
				}

				private static string GetFirstStatType(Unit unit)
				{
						string[] unitStatIDs = unit.UnitStatsArrayIDs;
						return GetFirstStatType(unitStatIDs);
				}
		
				public static string GetFirstStatType(string[] unitStatIDs)
				{
						return unitStatIDs[0];
				}
		
				private XmlElement CreateTable(SystemStats stats, XmlDocument doc)
				{
						XmlElement table = doc.CreateElement("table");
						XmlElement headerRow = doc.CreateElement("tr");
						table.AppendChild(headerRow);
						XmlElement name = doc.CreateElement("th");
						name.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitName", "name");
						headerRow.AppendChild(name);
			
						XmlElement unitTypeName = doc.CreateElement("th");
						unitTypeName.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitTypeName", "type name");
						headerRow.AppendChild(unitTypeName);
			
						foreach (StatSlot stat in stats.StatSlots)
						{
								XmlElement statHeader = doc.CreateElement("th");
								statHeader.InnerText = stat.Name;
								headerRow.AppendChild(statHeader);
						}
			
						XmlElement notes = doc.CreateElement("th");
						notes.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitNotes", "name");
						;
						headerRow.AppendChild(notes);
			
						XmlElement points = doc.CreateElement("th");
						points.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitPoints", "name");
						;
						headerRow.AppendChild(points);
			
						return table;
				}
		
				private XmlElement CreateUnitRow(Unit unit, XmlElement tableElem)
				{
						XmlDocument doc = tableElem.OwnerDocument;
						XmlElement row = doc.CreateElement("tr");
						tableElem.AppendChild(row);
						Stat[][] memberStats = unit.UnitStatsArraysWithName;
						string[] statTypeIDs = unit.UnitStatsArrayIDs;
						string defaultStatType = GetFirstStatType(statTypeIDs);
						int statRowCount = 0;
						bool hasOther = false;
			
						foreach (string statTypeID in statTypeIDs)
						{
								if (statTypeID.Equals(defaultStatType))
								{
										statRowCount++;
								} else if (!hasOther)
								{
										statRowCount++;
										hasOther = true;
								}
						}
			
						XmlElement name = doc.CreateElement("td");
						name.InnerText = unit.Name;
						SetRowSpan(name, statRowCount);
						row.AppendChild(name);
						CreateStatsBlock(row, memberStats, statTypeIDs);

						StringBuilder sb = new StringBuilder();
						UnitEquipmentItem[] unitEquipment = unit.GetEquipment();
			
						if (unitEquipment.Length > 0)
						{
								bool addSeparator = false;
				
								foreach (UnitEquipmentItem equip in unitEquipment)
								{
										if (!addSeparator)
										{
												addSeparator = true;
										} else
										{
												sb.Append(", ");
										}

										string amountString;
										double amount = UnitEquipmentUtil.GetEquipmentAmount(unit, equip);

										if (UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, equip))
										{

												if (amount == 100)
												{
														amountString = GetEquipmentAmountAllTranslation(unit);
												} else
												{
														int number = UnitEquipmentUtil.GetEquipmentAmountTaken(unit, equip);
														amountString = GetEquipmentAmountRatioTranslation(amount, number);
												}
										} else
										{
												if (amount == -1)
												{
														amountString = GetEquipmentAmountAllTranslation(unit);
												} else
												{
														amountString = GetEquipmentAmountNumberTranslation((int)amount);
												}
										}

										sb.Append(Translation.GetTranslation("armyHtmlExportEquipAmountRatio", "{0} for {1}", equip.Name, amountString));
								}
				
								sb.Append(". ");
						}
			
						ICollection<Ability> abilities = unit.Abilities;
			
						if (abilities.Count > 0)
						{
								bool addSeparator = false;
				
								foreach (Ability ability in abilities)
								{
										if (!addSeparator)
										{
												addSeparator = true;
										} else
										{
												sb.Append(", ");
										}
					
										sb.Append(ability.Name);
								}
				
								sb.Append(". ");
						}
			
						XmlElement notes = doc.CreateElement("td");
						notes.InnerText = sb.ToString();
						SetRowSpan(notes, statRowCount);
						row.AppendChild(notes);
			
						XmlElement points = doc.CreateElement("td");
						points.InnerText = unit.Points.ToString();
						SetRowSpan(points, statRowCount);
						row.AppendChild(points);
			
						return row;
				}
		
				private static void SetRowSpan(XmlElement xmlElement, int statRowCount)
				{
						if (statRowCount > 1)
						{
								xmlElement.SetAttribute("rowspan", statRowCount.ToString());
						}
				}
		
				private void CreateStatsBlock(XmlElement unitRow, Stat[][] memberStats, string[] statTypeIDs)
				{
						XmlDocument doc = unitRow.OwnerDocument;
						string defaultStatType = GetFirstStatType(statTypeIDs);
			
						Stat[] defaultStatLine = memberStats[0];
						int defaultStatLineCount = defaultStatLine.Length;
						AddStatCell(defaultStatLine[0].SlotValueString, unitRow);
			
						for (int i = 1; i < defaultStatLineCount; i++)
						{
								string statText = GetDefaultStatCellText(defaultStatLine[i]);
								AddStatCell(statText, unitRow);
						}
			
						int statCount = statTypeIDs.Length;
			
						if (statCount > 1)
						{
								XmlElement unitTable = (XmlElement)unitRow.ParentNode;
								Dictionary<string, XmlElement> statParents = CreateStatsParentElements(statTypeIDs, unitTable);
				
								for (int i = 1; i < statCount; i++)
								{
										Stat[] statLine = memberStats[i];
										string statTypeID = statTypeIDs[i];
										XmlElement tableElement = DictionaryUtils.GetValue(statParents, statTypeID);
										int statLineCount = statLine.Length;
										XmlElement statRow = doc.CreateElement("tr");
										tableElement.AppendChild(statRow);
										GetStatCellTextDelegate statCellTextDelegate = (statTypeID.Equals(defaultStatType) ? new GetStatCellTextDelegate(GetDefaultStatCellText) : new GetStatCellTextDelegate(GetOtherStatCellText));
										AddStatCell(statLine[0].SlotValueString, statRow);
				
										for (int j = 1; j < statLineCount; j++)
										{
												string statText = statCellTextDelegate(statLine[j]);
												AddStatCell(statText, statRow);
										}
								}
				
								if (statParents.Count > 1)
								{
										AddOtherUnitStatTables(statParents, unitTable, defaultStatLineCount);
								}
						}
				}
		
				private static void AddOtherUnitStatTables(Dictionary<string, XmlElement> statParents, XmlElement unitTable, int defaultStatLineCount)
				{
						XmlDocument doc = unitTable.OwnerDocument;
						XmlElement otherStatsRow = doc.CreateElement("tr");
						unitTable.AppendChild(otherStatsRow);
						XmlElement otherStatsCell = doc.CreateElement("td");
						otherStatsCell.SetAttribute("colspan", defaultStatLineCount.ToString());
						otherStatsRow.AppendChild(otherStatsCell);
			
						foreach (XmlElement tableElem in statParents.Values)
						{
								if (tableElem != unitTable)
								{
										otherStatsCell.AppendChild(tableElem);
								}
						}
				}

				private Dictionary<string, XmlElement> CreateStatsParentElements(string[] statTypeIDs, XmlElement parentTable)
				{
						Dictionary<string, XmlElement> statParents = new Dictionary<string, XmlElement>();
						XmlDocument doc = parentTable.OwnerDocument;
						string defaultStatTypeID = GetFirstStatType(statTypeIDs);
						statParents[defaultStatTypeID] = parentTable;
			
						foreach (string statTypeID in statTypeIDs)
						{
								if (!statParents.ContainsKey(statTypeID))
								{
										XmlElement tableElement = doc.CreateElement("table");
										statParents[statTypeID] = tableElement;
								}
						}
			
						return statParents;
				}

				private string GetDefaultStatCellText(Stat stat)
				{
						return Translation.GetTranslation("armyHtmlExportDefaultStatCellText", "{0}", stat.SlotValueString, stat.ParentSlotName);
				}

				private string GetOtherStatCellText(Stat stat)
				{
						return Translation.GetTranslation("armyHtmlExportOtherStatCellText", "{1}: {0}", stat.SlotValueString, stat.ParentSlotName);
				}
		
				private static void AddStatCell(string statValue, XmlElement row)
				{
						XmlElement statCell = row.OwnerDocument.CreateElement("td");
						statCell.InnerText = statValue;
						row.AppendChild(statCell);
				}
		
				private string GetEquipmentAmountRatioTranslation(double amount, int number)
				{
						return Translation.GetTranslation("armyHtmlExportEquipAmountPercentage", "{0}% ({1})", amount, number);
				}
		
				private string GetEquipmentAmountNumberTranslation(int amount)
				{
						return Translation.GetTranslation("armyHtmlExportEquipAmountNumber", "{0}", amount);
				}
		
				private string GetEquipmentAmountAllTranslation(Unit unit)
				{
						return Translation.GetTranslation("armyHtmlExportEquipAmountAll", "all ({1})", 100, unit.Size);
				}
		}
}