using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Schema;
using System.IO;
using System.Xml;
namespace Wayne.Lib.IO
{
///
/// A class that can be used to convert XML documents that conforms to the FlatFile.xsd into flat text files.
///
public class FlatFileFormatter
{
#region Fields
XmlSchemaSet schemaSet;
#endregion
#region Construction
///
/// Initializes a new intance of the Flat file formatter.
///
public FlatFileFormatter()
{
schemaSet = new XmlSchemaSet();
using (var schemaStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Wayne.Lib.IO.FlatFileFormatter.FlatFile.xsd"))
{
XmlSchema schema = XmlSchema.Read(schemaStream, null);
schemaSet.Add(schema);
schemaSet.Compile();
}
}
#endregion
#region Public methods
///
/// Creates a text file from the specified flat file XML document stream.
///
/// A stream that contains a Flat file XML document.
/// File that should be created by the formatting routine.
public void Format(Stream flatFileXmlStream, string fileName)
{
//Start by validating the input stream, so it is a correct flatfile XML file.
ValidateFile(flatFileXmlStream);
HandleXml(flatFileXmlStream, fileName);
}
#endregion
#region Private methods
private void ValidateFile(Stream flatFileXmlStream)
{
long startPosition = flatFileXmlStream.Position;
XmlReaderSettings validationReaderSettings = new XmlReaderSettings();
validationReaderSettings.Schemas = schemaSet;
validationReaderSettings.CloseInput = false;
//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
//'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
//in a full-framework environment.
if (System.Environment.OSVersion.Platform == PlatformID.WinCE)
validationReaderSettings.ValidationType = ValidationType.Schema;
else
validationReaderSettings.ValidationType = (System.Xml.ValidationType)4;
try
{
using (XmlReader validationReader = XmlReader.Create(flatFileXmlStream, validationReaderSettings))
{
while (validationReader.Read()) { }
}
}
catch (XmlSchemaException xmlSchemaException)
{
throw new FlatFileFormatException("Invalid flat file Xml. Xml schema validation failed.", xmlSchemaException);
}
flatFileXmlStream.Position = startPosition;
}
private void HandleXml(Stream flatFileXmlStream, string fileName)
{
try
{
string fieldSeparator = "";
string recordSeparator = "\r\n";
using (XmlReader reader = XmlReader.Create(flatFileXmlStream))
{
Dictionary predefinedFormats = new Dictionary();
using (Stream outputFile = FileSupport.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.Read, 100, 100))
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
{
if (reader.LocalName == "Section")
{
if (reader.MoveToAttribute("fieldSeparator"))
fieldSeparator = reader.Value;
if (reader.MoveToAttribute("recordSeparator"))
recordSeparator = reader.Value;
reader.MoveToElement();
}
else if (reader.LocalName == "FormatDefinitions")
HandleFormatDefinitions(reader, predefinedFormats);
else if (reader.LocalName == "Record")
HandleRecord(reader, predefinedFormats, outputFile, fieldSeparator, recordSeparator);
break;
}
case XmlNodeType.EndElement:
{
if (reader.LocalName == "FlatFile")
return;
break;
}
}
}
}
}
}
catch (Exception exception)
{
throw new FlatFileFormatException("Exception when formatting flat file.", exception);
}
}
private void HandleFormatDefinitions(XmlReader reader, Dictionary predefinedFormats)
{
bool moved = false;
while (true)
{
if (!moved)
if (!reader.Read())
break;
moved = false;
switch (reader.NodeType)
{
case XmlNodeType.Element:
{
if (reader.LocalName == "Format")
{
if (reader.MoveToAttribute("formatId"))
{
string formatId;
formatId = reader.Value;
reader.MoveToElement();
string format = reader.ReadElementContentAsString();
moved = true;
predefinedFormats.Add(formatId, format);
}
}
break;
}
case XmlNodeType.EndElement:
{
if (reader.LocalName == "FormatDefinitions")
return;
break;
}
}
}
}
#region Inner class Field
private class Field
{
#region Fields
object value;
string format;
#endregion
#region Construction
public Field(string type, string format, string value)
{
this.format = format;
#region Decode the type & value
switch (type)
{
case "String":
{
this.value = value;
break;
}
case "Boolean":
{
this.value = XmlConvert.ToBoolean(value);
break;
}
case "Byte":
{
this.value = XmlConvert.ToByte(value);
break;
}
case "Int16":
{
this.value = XmlConvert.ToInt16(value);
break;
}
case "Int32":
{
this.value = XmlConvert.ToInt32(value);
break;
}
case "Int64":
{
this.value = XmlConvert.ToInt64(value);
break;
}
case "UInt16":
{
this.value = XmlConvert.ToUInt64(value);
break;
}
case "UInt32":
{
this.value = XmlConvert.ToUInt32(value);
break;
}
case "UInt64":
{
this.value = XmlConvert.ToUInt64(value);
break;
}
case "Decimal":
{
this.value = XmlConvert.ToDecimal(value);
break;
}
case "Double":
{
this.value = XmlConvert.ToDouble(value);
break;
}
case "DateTime":
{
this.value = XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Unspecified);
break;
}
}
#endregion
}
#endregion
#region Properties
public object Value
{
get { return value; }
}
public string Format
{
get { return format; }
}
#endregion
}
#endregion
private void HandleRecord(XmlReader reader, Dictionary predefinedFormats, Stream outputFile, string fieldSeparator, string recordSeparator)
{
bool endRecordFound = false;
List fieldList = new List();
bool moved = false;
while (!endRecordFound)
{
if (!moved)
endRecordFound = !reader.Read();
moved = false;
if (!endRecordFound)
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
{
if (reader.LocalName == "Field")
{
string type = "String";
string format = null;
string definedFormatId = null;
string value;
if (reader.MoveToAttribute("type"))
type = reader.Value;
if (reader.MoveToAttribute("format"))
format = reader.Value;
if (reader.MoveToAttribute("formatId"))
definedFormatId = reader.Value;
reader.MoveToContent();
value = reader.ReadElementContentAsString();
moved = true;
//If there was no format defined, try to find a predefined format, if that was specified. Otherwise run with no format.
if ((format == null) && (definedFormatId != null))
predefinedFormats.TryGetValue(definedFormatId, out format);
Field field = new Field(type, format, value);
fieldList.Add(field);
}
break;
}
case XmlNodeType.Text:
{
}
break;
case XmlNodeType.EndElement:
{
if (reader.LocalName == "Record")
endRecordFound = true;
break;
}
}
}
}
//Time to save the data.
StringBuilder formatStringBuilder = new StringBuilder();
object[] valueList = new object[fieldList.Count];
for (int i = 0; i < fieldList.Count; i++)
{
valueList[i] = fieldList[i].Value;
formatStringBuilder.Append("{");
formatStringBuilder.Append(i.ToString(System.Globalization.CultureInfo.InvariantCulture));
//Append
if (fieldList[i].Format != null)
{
formatStringBuilder.Append(",");
formatStringBuilder.Append(fieldList[i].Format);
}
formatStringBuilder.Append("}");
//If this is the last field, we should not insert any field separator.
if (i != (fieldList.Count - 1))
formatStringBuilder.Append(fieldSeparator);
}
//Insert the record separator.
formatStringBuilder.Append(recordSeparator);
string formattedRecord = string.Format(System.Globalization.CultureInfo.InvariantCulture, formatStringBuilder.ToString(), valueList);
byte[] encodedData = Encoding.Default.GetBytes(formattedRecord);
outputFile.Write(encodedData, 0, encodedData.Length);
}
#endregion
}
}