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 | |
---|
5 | using System; |
---|
6 | using System.Collections.Generic; |
---|
7 | using System.IO; |
---|
8 | using System.Text; |
---|
9 | using System.Xml; |
---|
10 | using System.Xml.Schema; |
---|
11 | using IBBoard.Lang; |
---|
12 | using IBBoard.Xml; |
---|
13 | using IBBoard.WarFoundry.API.Objects; |
---|
14 | using IBBoard.WarFoundry.API.Util; |
---|
15 | |
---|
16 | namespace 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 | } |
---|