FlatFileFormatter.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Xml.Schema;
  5. using System.IO;
  6. using System.Xml;
  7. namespace Wayne.Lib.IO
  8. {
  9. /// <summary>
  10. /// A class that can be used to convert XML documents that conforms to the FlatFile.xsd into flat text files.
  11. /// </summary>
  12. public class FlatFileFormatter
  13. {
  14. #region Fields
  15. XmlSchemaSet schemaSet;
  16. #endregion
  17. #region Construction
  18. /// <summary>
  19. /// Initializes a new intance of the Flat file formatter.
  20. /// </summary>
  21. public FlatFileFormatter()
  22. {
  23. schemaSet = new XmlSchemaSet();
  24. using (var schemaStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Wayne.Lib.IO.FlatFileFormatter.FlatFile.xsd"))
  25. {
  26. XmlSchema schema = XmlSchema.Read(schemaStream, null);
  27. schemaSet.Add(schema);
  28. schemaSet.Compile();
  29. }
  30. }
  31. #endregion
  32. #region Public methods
  33. /// <summary>
  34. /// Creates a text file from the specified flat file XML document stream.
  35. /// </summary>
  36. /// <param name="flatFileXmlStream">A stream that contains a Flat file XML document.</param>
  37. /// <param name="fileName">File that should be created by the formatting routine.</param>
  38. public void Format(Stream flatFileXmlStream, string fileName)
  39. {
  40. //Start by validating the input stream, so it is a correct flatfile XML file.
  41. ValidateFile(flatFileXmlStream);
  42. HandleXml(flatFileXmlStream, fileName);
  43. }
  44. #endregion
  45. #region Private methods
  46. private void ValidateFile(Stream flatFileXmlStream)
  47. {
  48. long startPosition = flatFileXmlStream.Position;
  49. XmlReaderSettings validationReaderSettings = new XmlReaderSettings();
  50. validationReaderSettings.Schemas = schemaSet;
  51. validationReaderSettings.CloseInput = false;
  52. //There is a bug in the compact/full framework boundary that makes, that if we set the validation type to Schema, it is actually set to
  53. //'Auto'. Therefore we need to do this very hack and check if we are running compact framework for real or if we are running a CF assembly
  54. //in a full-framework environment.
  55. if (System.Environment.OSVersion.Platform == PlatformID.WinCE)
  56. validationReaderSettings.ValidationType = ValidationType.Schema;
  57. else
  58. validationReaderSettings.ValidationType = (System.Xml.ValidationType)4;
  59. try
  60. {
  61. using (XmlReader validationReader = XmlReader.Create(flatFileXmlStream, validationReaderSettings))
  62. {
  63. while (validationReader.Read()) { }
  64. }
  65. }
  66. catch (XmlSchemaException xmlSchemaException)
  67. {
  68. throw new FlatFileFormatException("Invalid flat file Xml. Xml schema validation failed.", xmlSchemaException);
  69. }
  70. flatFileXmlStream.Position = startPosition;
  71. }
  72. private void HandleXml(Stream flatFileXmlStream, string fileName)
  73. {
  74. try
  75. {
  76. string fieldSeparator = "";
  77. string recordSeparator = "\r\n";
  78. using (XmlReader reader = XmlReader.Create(flatFileXmlStream))
  79. {
  80. Dictionary<string, string> predefinedFormats = new Dictionary<string, string>();
  81. using (Stream outputFile = FileSupport.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.Read, 100, 100))
  82. {
  83. while (reader.Read())
  84. {
  85. switch (reader.NodeType)
  86. {
  87. case XmlNodeType.Element:
  88. {
  89. if (reader.LocalName == "Section")
  90. {
  91. if (reader.MoveToAttribute("fieldSeparator"))
  92. fieldSeparator = reader.Value;
  93. if (reader.MoveToAttribute("recordSeparator"))
  94. recordSeparator = reader.Value;
  95. reader.MoveToElement();
  96. }
  97. else if (reader.LocalName == "FormatDefinitions")
  98. HandleFormatDefinitions(reader, predefinedFormats);
  99. else if (reader.LocalName == "Record")
  100. HandleRecord(reader, predefinedFormats, outputFile, fieldSeparator, recordSeparator);
  101. break;
  102. }
  103. case XmlNodeType.EndElement:
  104. {
  105. if (reader.LocalName == "FlatFile")
  106. return;
  107. break;
  108. }
  109. }
  110. }
  111. }
  112. }
  113. }
  114. catch (Exception exception)
  115. {
  116. throw new FlatFileFormatException("Exception when formatting flat file.", exception);
  117. }
  118. }
  119. private void HandleFormatDefinitions(XmlReader reader, Dictionary<string, string> predefinedFormats)
  120. {
  121. bool moved = false;
  122. while (true)
  123. {
  124. if (!moved)
  125. if (!reader.Read())
  126. break;
  127. moved = false;
  128. switch (reader.NodeType)
  129. {
  130. case XmlNodeType.Element:
  131. {
  132. if (reader.LocalName == "Format")
  133. {
  134. if (reader.MoveToAttribute("formatId"))
  135. {
  136. string formatId;
  137. formatId = reader.Value;
  138. reader.MoveToElement();
  139. string format = reader.ReadElementContentAsString();
  140. moved = true;
  141. predefinedFormats.Add(formatId, format);
  142. }
  143. }
  144. break;
  145. }
  146. case XmlNodeType.EndElement:
  147. {
  148. if (reader.LocalName == "FormatDefinitions")
  149. return;
  150. break;
  151. }
  152. }
  153. }
  154. }
  155. #region Inner class Field
  156. private class Field
  157. {
  158. #region Fields
  159. object value;
  160. string format;
  161. #endregion
  162. #region Construction
  163. public Field(string type, string format, string value)
  164. {
  165. this.format = format;
  166. #region Decode the type & value
  167. switch (type)
  168. {
  169. case "String":
  170. {
  171. this.value = value;
  172. break;
  173. }
  174. case "Boolean":
  175. {
  176. this.value = XmlConvert.ToBoolean(value);
  177. break;
  178. }
  179. case "Byte":
  180. {
  181. this.value = XmlConvert.ToByte(value);
  182. break;
  183. }
  184. case "Int16":
  185. {
  186. this.value = XmlConvert.ToInt16(value);
  187. break;
  188. }
  189. case "Int32":
  190. {
  191. this.value = XmlConvert.ToInt32(value);
  192. break;
  193. }
  194. case "Int64":
  195. {
  196. this.value = XmlConvert.ToInt64(value);
  197. break;
  198. }
  199. case "UInt16":
  200. {
  201. this.value = XmlConvert.ToUInt64(value);
  202. break;
  203. }
  204. case "UInt32":
  205. {
  206. this.value = XmlConvert.ToUInt32(value);
  207. break;
  208. }
  209. case "UInt64":
  210. {
  211. this.value = XmlConvert.ToUInt64(value);
  212. break;
  213. }
  214. case "Decimal":
  215. {
  216. this.value = XmlConvert.ToDecimal(value);
  217. break;
  218. }
  219. case "Double":
  220. {
  221. this.value = XmlConvert.ToDouble(value);
  222. break;
  223. }
  224. case "DateTime":
  225. {
  226. this.value = XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Unspecified);
  227. break;
  228. }
  229. }
  230. #endregion
  231. }
  232. #endregion
  233. #region Properties
  234. public object Value
  235. {
  236. get { return value; }
  237. }
  238. public string Format
  239. {
  240. get { return format; }
  241. }
  242. #endregion
  243. }
  244. #endregion
  245. private void HandleRecord(XmlReader reader, Dictionary<string, string> predefinedFormats, Stream outputFile, string fieldSeparator, string recordSeparator)
  246. {
  247. bool endRecordFound = false;
  248. List<Field> fieldList = new List<Field>();
  249. bool moved = false;
  250. while (!endRecordFound)
  251. {
  252. if (!moved)
  253. endRecordFound = !reader.Read();
  254. moved = false;
  255. if (!endRecordFound)
  256. {
  257. switch (reader.NodeType)
  258. {
  259. case XmlNodeType.Element:
  260. {
  261. if (reader.LocalName == "Field")
  262. {
  263. string type = "String";
  264. string format = null;
  265. string definedFormatId = null;
  266. string value;
  267. if (reader.MoveToAttribute("type"))
  268. type = reader.Value;
  269. if (reader.MoveToAttribute("format"))
  270. format = reader.Value;
  271. if (reader.MoveToAttribute("formatId"))
  272. definedFormatId = reader.Value;
  273. reader.MoveToContent();
  274. value = reader.ReadElementContentAsString();
  275. moved = true;
  276. //If there was no format defined, try to find a predefined format, if that was specified. Otherwise run with no format.
  277. if ((format == null) && (definedFormatId != null))
  278. predefinedFormats.TryGetValue(definedFormatId, out format);
  279. Field field = new Field(type, format, value);
  280. fieldList.Add(field);
  281. }
  282. break;
  283. }
  284. case XmlNodeType.Text:
  285. {
  286. }
  287. break;
  288. case XmlNodeType.EndElement:
  289. {
  290. if (reader.LocalName == "Record")
  291. endRecordFound = true;
  292. break;
  293. }
  294. }
  295. }
  296. }
  297. //Time to save the data.
  298. StringBuilder formatStringBuilder = new StringBuilder();
  299. object[] valueList = new object[fieldList.Count];
  300. for (int i = 0; i < fieldList.Count; i++)
  301. {
  302. valueList[i] = fieldList[i].Value;
  303. formatStringBuilder.Append("{");
  304. formatStringBuilder.Append(i.ToString(System.Globalization.CultureInfo.InvariantCulture));
  305. //Append
  306. if (fieldList[i].Format != null)
  307. {
  308. formatStringBuilder.Append(",");
  309. formatStringBuilder.Append(fieldList[i].Format);
  310. }
  311. formatStringBuilder.Append("}");
  312. //If this is the last field, we should not insert any field separator.
  313. if (i != (fieldList.Count - 1))
  314. formatStringBuilder.Append(fieldSeparator);
  315. }
  316. //Insert the record separator.
  317. formatStringBuilder.Append(recordSeparator);
  318. string formattedRecord = string.Format(System.Globalization.CultureInfo.InvariantCulture, formatStringBuilder.ToString(), valueList);
  319. byte[] encodedData = Encoding.Default.GetBytes(formattedRecord);
  320. outputFile.Write(encodedData, 0, encodedData.Length);
  321. }
  322. #endregion
  323. }
  324. }