Ticket #314: WarFoundryHtmlExporter.cs

File WarFoundryHtmlExporter.cs, 11.6 KB (added by Andrea Bonisoli, 9 years ago)

corrected file

Line 
1// This file (WarFoundryHtmlExporter.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
2//
3// 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.
4
5using System;
6using System.Collections.Generic;
7using System.IO;
8using System.Text;
9using System.Xml;
10using System.Xml.Schema;
11using IBBoard.Lang;
12using IBBoard.Xml;
13using IBBoard.WarFoundry.API.Objects;
14using IBBoard.WarFoundry.API.Util;
15
16namespace IBBoard.WarFoundry.API.Exporters
17{
18    /// <summary>
19    /// Custom exporter that exports an army as a basic HTML file
20    /// </summary>
21    public class WarFoundryHtmlExporter : IWarFoundryExporter
22    {
23        private static WarFoundryHtmlExporter exporter;
24        private delegate string GetStatCellTextDelegate(Stat stat);
25       
26        public static WarFoundryHtmlExporter GetDefault()
27        {
28            if (exporter == null)
29            {
30                exporter = new WarFoundryHtmlExporter();
31            }
32           
33            return exporter;
34        }
35       
36        private WarFoundryHtmlExporter()
37        {
38            //Hide constructor
39        }
40       
41        public void ExportArmy(Army army, string path)
42        {
43            XmlDocument doc = new XmlDocument();
44            CustomXmlResolver resolver = new CustomXmlResolver();
45            resolver.AddMapping("-//W3C//DTD XHTML 1.0 Strict//EN", new Uri("file://" + IBBoard.Constants.ExecutablePath + "/schemas/xhtml1-strict.dtd"));
46            doc.XmlResolver = resolver;
47            doc.AppendChild(doc.CreateDocumentType("html", "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null));
48            XmlElement html = doc.CreateElement("html");
49            doc.AppendChild(html);
50            XmlElement head = doc.CreateElement("head");
51            html.AppendChild(head);
52            XmlElement title = doc.CreateElement("title");
53            title.InnerXml = army.Name;
54            head.AppendChild(title);
55            XmlElement metaCharset = doc.CreateElement("meta");
56            metaCharset.SetAttribute("http-equiv", "Content-Type");
57            metaCharset.SetAttribute("content", "text/html;charset=UTF-8");
58            head.AppendChild(metaCharset);
59            XmlElement style = doc.CreateElement("style");
60            style.InnerText = "table, th, td { border: 1px solid #000; border-spacing: 0; border-collapse: collapse; margin: 0 }\n"
61                +"table table { width: 100%; border-width: 0; margin: -2px }\n"
62                +"table table td { border-width:0 1px }";
63            head.AppendChild(style);
64            XmlElement body = doc.CreateElement("body");
65            html.AppendChild(body);
66            XmlElement header = doc.CreateElement("h1");
67            header.InnerText = Translation.GetTranslation("armyHtmlOutputBodyHeader", "{0} - {1}pts", army.Name, army.Points);
68            body.AppendChild(header);
69           
70            foreach (XmlElement table in CreateTables(army, doc))
71            {
72                if (!IsTableOnlyHeader(table))
73                {
74                    body.AppendChild(table);
75                }
76            }
77           
78            StreamWriter writer = new StreamWriter(path, false);
79           
80            try
81            {
82                writer.Write(doc.OuterXml);
83            }
84            finally
85            {
86                writer.Close();
87            }
88        }
89
90        private bool IsTableOnlyHeader(XmlElement table)
91        {
92            return table.ChildNodes.Count == 1;
93        }
94       
95        private XmlElement[] CreateTables(Army army, XmlDocument doc)
96        {
97            Dictionary<string, XmlElement> tables = new Dictionary<string, XmlElement>();
98           
99            foreach (SystemStats statSets in army.GameSystem.SystemStats)
100            {
101                tables[statSets.ID] = CreateTable(statSets, doc);
102            }
103           
104            foreach (Unit unit in army.GetUnits())
105            {
106                CreateUnitRow(unit, tables[GetFirstStatType(unit)]);
107            }
108           
109            return DictionaryUtils.ToArray(tables);
110        }
111
112        private static string GetFirstStatType(Unit unit)
113        {
114            string[] unitStatIDs = unit.UnitStatsArrayIDs;
115            return GetFirstStatType(unitStatIDs);
116        }
117       
118        public static string GetFirstStatType(string[] unitStatIDs)
119        {
120            return unitStatIDs[0];
121        }
122       
123        private XmlElement CreateTable(SystemStats stats, XmlDocument doc)
124        {
125            XmlElement table = doc.CreateElement("table");
126            XmlElement headerRow = doc.CreateElement("tr");
127            table.AppendChild(headerRow);
128            XmlElement name = doc.CreateElement("th");
129            name.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitName", "name");
130            headerRow.AppendChild(name);
131           
132            XmlElement unitTypeName = doc.CreateElement("th");
133            unitTypeName.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitTypeName", "type name");
134            headerRow.AppendChild(unitTypeName);
135           
136            foreach (StatSlot stat in stats.StatSlots)
137            {
138                XmlElement statHeader = doc.CreateElement("th");
139                statHeader.InnerText = stat.Name;
140                headerRow.AppendChild(statHeader);
141            }
142           
143            XmlElement notes = doc.CreateElement("th");
144            notes.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitNotes", "name");;
145            headerRow.AppendChild(notes);
146           
147            XmlElement points = doc.CreateElement("th");
148            points.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitPoints", "name");;
149            headerRow.AppendChild(points);
150           
151            return table;
152        }
153       
154        private XmlElement CreateUnitRow(Unit unit, XmlElement tableElem)
155        {
156            XmlDocument doc = tableElem.OwnerDocument;
157            XmlElement row = doc.CreateElement("tr");
158            tableElem.AppendChild(row);
159            Stat[][] memberStats = unit.UnitStatsArraysWithName;
160            string[] statTypeIDs = unit.UnitStatsArrayIDs;
161            string defaultStatType = GetFirstStatType(statTypeIDs);
162            int statRowCount = 0;
163            bool hasOther = false;
164           
165            foreach (string statTypeID in statTypeIDs)
166            {
167                if (statTypeID.Equals(defaultStatType))
168                {
169                    statRowCount++;
170                }
171                else if (!hasOther)
172                {
173                    statRowCount++;
174                    hasOther = true;
175                }
176            }
177           
178            XmlElement name = doc.CreateElement("td");
179            name.InnerText = unit.Name;
180            SetRowSpan(name, statRowCount);
181            row.AppendChild(name);
182            CreateStatsBlock(row, memberStats, statTypeIDs);
183
184            StringBuilder sb = new StringBuilder();
185            UnitEquipmentItem[] unitEquipment = unit.GetEquipment();
186           
187            if (unitEquipment.Length > 0)
188            {
189                bool addSeparator = false;
190               
191                foreach (UnitEquipmentItem equip in unitEquipment)
192                {
193                    if (!addSeparator)
194                    {
195                        addSeparator = true;
196                    }
197                    else
198                    {
199                        sb.Append(", ");
200                    }
201
202                    string amountString;
203                    double amount = UnitEquipmentUtil.GetEquipmentAmount(unit, equip);
204
205                    if (UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, equip))
206                    {
207
208                        if (amount == 100)
209                        {
210                            amountString = GetEquipmentAmountAllTranslation(unit);
211                        }
212                        else
213                        {
214                            int number = UnitEquipmentUtil.GetEquipmentAmountTaken(unit, equip);
215                            amountString = GetEquipmentAmountRatioTranslation(amount, number);
216                        }
217                    }
218                    else
219                    {
220                        if (amount == -1)
221                        {
222                            amountString = GetEquipmentAmountAllTranslation(unit);
223                        }
224                        else
225                        {
226                            amountString = GetEquipmentAmountNumberTranslation((int)amount);
227                        }
228                    }
229
230                    sb.Append(Translation.GetTranslation("armyHtmlExportEquipAmountRatio", "{0} for {1}", equip.Name, amountString));
231                }
232               
233                sb.Append(". ");
234            }
235           
236            ICollection<Ability> abilities = unit.Abilities;
237           
238            if (abilities.Count > 0)
239            {
240                bool addSeparator = false;
241               
242                foreach (Ability ability in abilities)
243                {
244                    if (!addSeparator)
245                    {
246                        addSeparator = true;
247                    }
248                    else
249                    {
250                        sb.Append(", ");
251                    }
252                   
253                    sb.Append(ability.Name);
254                }
255               
256                sb.Append(". ");
257            }
258           
259            XmlElement notes = doc.CreateElement("td");
260            notes.InnerText = sb.ToString();
261            SetRowSpan(notes, statRowCount);
262            row.AppendChild(notes);
263           
264            XmlElement points = doc.CreateElement("td");
265            points.InnerText = unit.Points.ToString();
266            SetRowSpan(points, statRowCount);
267            row.AppendChild(points);
268           
269            return row;
270        }
271       
272        private static void SetRowSpan(XmlElement xmlElement, int statRowCount)
273        {
274            if (statRowCount > 1)
275            {
276                xmlElement.SetAttribute("rowspan", statRowCount.ToString());
277            }
278        }
279       
280        private void CreateStatsBlock(XmlElement unitRow, Stat[][] memberStats, string[] statTypeIDs)
281        {
282            XmlDocument doc = unitRow.OwnerDocument;
283            string defaultStatType = GetFirstStatType(statTypeIDs);
284           
285            Stat[] defaultStatLine = memberStats[0];
286            int defaultStatLineCount = defaultStatLine.Length;
287            AddStatCell(defaultStatLine[0].SlotValueString, unitRow);
288           
289            for (int i = 1; i < defaultStatLineCount; i++)
290            {
291                string statText = GetDefaultStatCellText(defaultStatLine[i]);
292                AddStatCell(statText, unitRow);
293            }
294           
295            int statCount = statTypeIDs.Length;
296           
297            if (statCount > 1)
298            {
299                XmlElement unitTable = (XmlElement)unitRow.ParentNode;
300                Dictionary<string, XmlElement> statParents = CreateStatsParentElements(statTypeIDs, unitTable);
301               
302                for (int i = 1; i < statCount; i++)
303                {
304                    Stat[] statLine = memberStats[i];
305                    string statTypeID = statTypeIDs[i];
306                    XmlElement tableElement = DictionaryUtils.GetValue(statParents, statTypeID);
307                    int statLineCount = statLine.Length;
308                    XmlElement statRow = doc.CreateElement("tr");
309                    tableElement.AppendChild(statRow);
310                    GetStatCellTextDelegate statCellTextDelegate = (statTypeID.Equals(defaultStatType) ? new GetStatCellTextDelegate(GetDefaultStatCellText) : new GetStatCellTextDelegate(GetOtherStatCellText));
311                    AddStatCell(statLine[0].SlotValueString, statRow);
312               
313                    for (int j = 1; j < statLineCount; j++)
314                    {
315                        string statText = statCellTextDelegate(statLine[j]);
316                        AddStatCell(statText, statRow);
317                    }
318                }
319               
320                if (statParents.Count > 1)
321                {
322                    AddOtherUnitStatTables(statParents, unitTable, defaultStatLineCount);
323                }
324            }
325        }
326       
327        private static void AddOtherUnitStatTables(Dictionary<string, XmlElement> statParents, XmlElement unitTable, int defaultStatLineCount)
328        {
329            XmlDocument doc = unitTable.OwnerDocument;
330            XmlElement otherStatsRow = doc.CreateElement("tr");
331            unitTable.AppendChild(otherStatsRow);
332            XmlElement otherStatsCell = doc.CreateElement("td");
333            otherStatsCell.SetAttribute("colspan", defaultStatLineCount.ToString());
334            otherStatsRow.AppendChild(otherStatsCell);
335           
336            foreach (XmlElement tableElem in statParents.Values)
337            {
338                if (tableElem != unitTable)
339                {
340                    otherStatsCell.AppendChild(tableElem);
341                }
342            }
343        }
344
345        private Dictionary<string, XmlElement> CreateStatsParentElements(string[] statTypeIDs, XmlElement parentTable)
346        {
347            Dictionary<string, XmlElement> statParents = new Dictionary<string, XmlElement>();
348            XmlDocument doc = parentTable.OwnerDocument;
349            string defaultStatTypeID = GetFirstStatType(statTypeIDs);
350            statParents[defaultStatTypeID] = parentTable;
351           
352            foreach (string statTypeID in statTypeIDs)
353            {
354                if (!statParents.ContainsKey(statTypeID))
355                {
356                    XmlElement tableElement = doc.CreateElement("table");
357                    statParents[statTypeID] = tableElement;
358                }
359            }
360           
361            return statParents;
362        }
363
364        private string GetDefaultStatCellText(Stat stat)
365        {
366            return Translation.GetTranslation("armyHtmlExportDefaultStatCellText", "{0}", stat.SlotValueString, stat.ParentSlotName);
367        }
368
369        private string GetOtherStatCellText(Stat stat)
370        {
371            return Translation.GetTranslation("armyHtmlExportOtherStatCellText", "{1}: {0}", stat.SlotValueString, stat.ParentSlotName);
372        }
373       
374        private static void AddStatCell(string statValue, XmlElement row)
375        {
376            XmlElement statCell = row.OwnerDocument.CreateElement("td");
377            statCell.InnerText = statValue;
378            row.AppendChild(statCell);
379        }
380       
381        private string GetEquipmentAmountRatioTranslation (double amount, int number)
382        {
383            return Translation.GetTranslation ("armyHtmlExportEquipAmountPercentage", "{0}% ({1})", amount, number);
384        }
385       
386        private string GetEquipmentAmountNumberTranslation(int amount)
387        {
388            return Translation.GetTranslation("armyHtmlExportEquipAmountNumber", "{0}", amount);
389        }
390       
391        private string GetEquipmentAmountAllTranslation(Unit unit)
392        {
393            return Translation.GetTranslation("armyHtmlExportEquipAmountAll", "all ({1})", 100, unit.Size);
394        }
395    }
396}