HotDog's Blog

Hotdog (Robert Verpalen) about C# and vb.net

This blog hosted by:
http://blogs.vbcity.com      
  Home :: Syndication  :: Login

NovDecember 2005Jan
SMTWTFS
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

Articles

Archives

Topics

CONTACT

Fun but useful linkies

General

VS 2005

Wolfenstein ET

Thursday, December 08, 2005 #

MessageQueue: wrapper to show info messages in their own thread. Right now 2 simple modes are included: modal mode and outlookstyle. The latter scrolling up the message from the bottom right corner.
The modal style really only is in for some backward compatibility in another project, but the scroll up mode is the main goal. It's a simple way to show more or less important messages without interrupting the main program or to show messages received from a server.
As it is build now the message push each other up until the screen is filled, then filled up again only if messages are closed. At the moment no timer is implemented for closing a message, but that can be implemented easily enough in the MessageForm class

Last update 10-12-2005
MessageQueue code (C# 2.0) : Messagequeue code
Download example project (C# 2.0): Messages.zip

posted @ 6:08 AM

This class as the title implies reads fixed length files into classes. The classes use attributes to define themselves as belonging to the Fixed Length File. Sorry, the comments in this case are still mostly in Dutch, you might be able to filter the examples if you don't speak the language, but if you do, that would be easier ;-)
Will try to do the translation later on.

Code CopyHideScrollFull
using System;
using
System.Collections;
using
System.Reflection;
using
System.IO;
using
ImportExport;
using
System.Globalization;
using
System.Collections.Generic;

namespace
ImportExport
{
/// <summary>
///
Just a base class to include statics and classes that shouldn't be included
///
in the generic FixedLengthReader.
///
</summary>
public
abstract class FixedLengthReader
{
public readonly Type Base;
public
FixedLengthReader(Type Base,int? Version)
{
this.Base = Base;
}

#region Version
/// <summary>
///
Optional.
///
Use this property to indicate the version of the file.
///
If one of the fieldattributes (<see cref="FLRAttribute"/> contains a specific
///
version, that field is only used if it corresponds with this number.
///

///
NB: changing this value after the constructor has finished, does not do anything for
///
the field versions
///
</summary>
protected
int? version;
/// <summary>
///
Optional.
///
Use this property to indicate the version of the file.
///
If one of the fieldattributes (<see cref="FLRAttribute"/> contains a specific
///
version, that field is only used if it corresponds with this number
///
</summary>
public
int? Version { get { return version; } }
#endregion
/// <summary>
///
Represents a field of the RecordType of the FixedLengthReader
///
</summary>
protected
class Field
{
public Field(FieldInfo fi, FLRFieldAttribute fa)
{
this.FieldInfo = fi;
if
(fa.Parser != null)
this.Parser = ParserBase.GetParser(fa.Parser);
}

public ParserBase Parser;
public
int Position = -1;
public
int Length = -1;
public
int FirstPositionNextField
{
get { return Position + Length; }
}
public
readonly FieldInfo FieldInfo;
public
Field Next;
public override int GetHashCode()
{
return Position;
}
}
protected abstract class ChildBase
{
public readonly FieldInfo FieldInfo;
public
readonly ChildAttribute ca;

public
ChildBase(FieldInfo fi, ChildAttribute ca)
{
this.FieldInfo = fi;
this
.ca = ca;
}
public abstract bool Fill(object ObjectToFill, Stream s);
public static ChildBase CreateChild(FieldInfo fi, ChildAttribute ca)
{
return (ChildBase)Activator.CreateInstance(
typeof(Child<>).MakeGenericType(fi.FieldType), fi, ca);
}
}
protected class Child<RecordType>:ChildBase
where RecordType:new()
{
public readonly FixedLengthReader<RecordType> flr;
public
Child(FieldInfo fi, ChildAttribute ca):base(fi,ca)
{
this.flr = new FixedLengthReader<RecordType>();
}
public override bool Fill(object ObjectToFill, Stream s)
{
return Fill((RecordType)ObjectToFill, s);
}
public
virtual bool Fill(RecordType ObjectToFill, Stream s)
{
if (ca.IsMatch(s))
{
//TODO: collecties...
RecordType o = flr.getInstance();
flr.Fill(o, s);
FieldInfo.SetValue(ObjectToFill, o);
return
true;
}
else
return false;
}
}
protected class ParserCollection
{
struct ParserInstance
{
public readonly int TypeCode;
public
readonly Type TargetType;
Type
parsertype;
public
Type ParserType
{
get { return parsertype; }
set

{
if (value == parsertype) return;
if
(value == null) throw new ArgumentNullException();
ParserBase
.CheckType(value);
parsertype = value;
instance = null;
}
}
public
ParserInstance(Type TargetType, Type ParserType)
{
this.TargetType = TargetType;
this
.parsertype = null;
this
.instance = null;
this
.TypeCode = TargetType.GetHashCode();
this.ParserType = ParserType;
}
ParserBase instance;
public
ParserBase GetInstance()
{
if (instance == null) instance = (ParserBase)Activator.CreateInstance(parsertype);
return
instance;
}
}
List<ParserInstance> list = new List<ParserInstance>();
public ParserCollection()
{
//asign default type parsers (these can be overriden by using the defaultparser attributes)
Type
pt = typeof(TextParser);
list.Add(new ParserInstance(typeof(string), pt));
pt = typeof(IntegerParser);
list.Add(new ParserInstance(typeof(int), pt));
list.Add(new ParserInstance(typeof(long), pt));
pt = typeof(DateTimeExactParser);
list.Add(new ParserInstance(typeof(DateTime), pt));
pt = typeof(BoolParser);
list.Add(new ParserInstance(typeof(bool), pt));
}

public
void Add(Type TargetType, Type ParserType)
{
this[TargetType] = ParserType;
}
public int IndexOf(Type TargetType)
{
for (int i = 0; i < list.Count; i++)
{
if (list[i].TargetType == TargetType)
return i;
}
return
-1;
}
public Type this[Type TargetType]
{
get
{
int i = IndexOf(TargetType);
if
(i == -1) return null;
return
list[i].ParserType;
}
set

{
int i = IndexOf(TargetType);
ParserInstance
pi = new ParserInstance(TargetType, value);
if
(i == -1)
list.Add(pi);
else
list[i] = pi;
}
}
public ParserBase GetParser<T>()
{
return this[typeof(T)];
}
internal ParserBase GetParser(Type TargetType)
{
int i = IndexOf(TargetType);
if
(i == -1)
throw new Exception("No parser assigned for type: " + TargetType.FullName);
return list[i].GetInstance();
}
internal bool TryGetParser(Type TargetType, out ParserBase parser)
{
try
{
parser = GetParser(TargetType);
return
true;
}
catch

{
parser = null;
return
false;
}
}
}
}
///
<summary>Algemene klasse om snel 'fixed length' tekstbestanden in te kunnen lezen
///
Elke klasse die velden bevat waaraan ofwel het PositionAttribute, ofwel het FixedLengthAttribute zijn toegekend
///
kunnen gebruikt worden om mbv deze klasse in te lezen</summary>
///

///
Een heel bestand kan worden ingelezen met de ReadFile methode. Dit is tevens de snelste methode om
///
een bestand in te lezen, omdat per blok alleen de vereiste bytes worden ingezen en dus geen strings
///
op en neer hoeven te worden gestuurd en het kopieren van strings voorkomen kan worden (.net behandeld
///
strings als valuetypes. Een toewijzing van string a = string b veroorzaakt al een kopie)
///

///
De eerste stap is een klasse die kan worden ingelezen, dit is simpelweg een kwestie van
///
een FixedLengthAttribute toekennen aan de juiste velden.
///
<remarks></remarks>
///
<example>
///
class AClass
///
{
///
        [FixedLength(35)]
///
        public string AStringvalue;
///
        [FixedLength(3)]
///
        public double AdoubleValue;
///
        //etc.
///
}
///

///
//om de volledige lijst terug te krijgen:
///
ArrayList AClasses = FixedLengthReader.ReadFile(typeof(AClass),"NameOfTheFile");
///

///
//da's al :)
///
//uiteraard kan ook een instantie worden gecreeerd van de FLR en daarmee worden gelezen,
///
//maar in het algemeen zal dit voldoen en uiteraard worden straks generics gebruikt ;-)
///

///
//Een enkele regel kan uitgelezen worden met ReadLine() Dit kan rechstreeks van de stream, maar ook van een string
///

///

///
//Als een regel in het bestand een vaste lengte heeft kan aan de klasse zelf een RowLengthAttribute
///
//toegekend worden. bijv.
///
[RowLength(2048)]
///
class AClass
///
{
///
}
///

///
/*
///
Numerieke waarden omzetten. bij numerieke waarde gaat het natuurlijk vooral om de gebroken getallen
///
Standaard wordt de <see cref="FloatParser"/> gebruikt voor gebroken getallen, maar die kan of per veld
///
of op klasse niveau anders toegekend worden. Op klasse niveau: */
///
[DefaultFloatParser(typeof(FloatDivide100))]
///
class AClass
///
{
///
}
///

///
// Per Veld kan ook een specifieke parser worden toegekend (dit kan niet alleen voor de gebroken
///
// getallen, maar voor alle velden), bijv
///
[FixedLength(4,Parser = typeof(EenDoubleParser))]
///
public double AdoubleValue;
///

///
// Ten slotte kan ook op klasse niveau een parser toegekend worden voor een bepaald type mbv
///
// het <see cref="DefaultParserAttribute"/>. Het 1e type is het type veld voor welke
///
// die parser gebruikt moet worden, het 2e type het type van de parser.
///
// Uiteraard geldt ook hier weer, dat Parsers toegekend per veld voorrang krijgen.
///
// Mbv de DefaultParserAttribute, kan bijv een Parser worden toegekend voor alle DateTime velden
///
</example>
public
class FixedLengthReader<RecordType>:FixedLengthReader
//where RecordType : new()
{        

readonly
Field firstfield;
readonly
List<ChildBase> children;
readonly
int RowLength=-1;        
readonly
ErrorMode emode;
readonly
bool ReadToEndOfLine = true;
///
<summary>
///
This field contains the default <see cref="ErrorMode"/> for newly created
///
<see cref="FixedLengthReader"/>s when no errormode is specified
///
</summary>
public
static ErrorMode DefaultErrorMode = ErrorMode.Throw;
public
FixedLengthReader():this(null)
{
}
public
FixedLengthReader(ErrorMode ErrorMode):this(ErrorMode, null)
{
}
public
FixedLengthReader(int? Version)
: this(DefaultErrorMode, Version)
{
}
#region supporting attribute functions
T GetAttribute<T>()
where T:FLRAttribute
{
return GetAttribute<T>(Base);
}
static T GetAttribute<T>(MemberInfo mi)
where T:FLRAttribute
{
foreach (T t in GetAttributes<T>(mi))
{
return t;
}
return
null;
}
IEnumerable<T> GetAttributes<T>()
where T : FLRAttribute
{
return GetAttributes<T>(Base);
}
IEnumerable<FLRAttribute> GetAttributes()
{
return GetAttributes<FLRAttribute>();
}
static IEnumerable<T> GetAttributes<T>(MemberInfo mi)
where T : FLRAttribute
{
foreach (object o in mi.GetCustomAttributes(typeof(T), true))
{
yield return (T)o;
}
}
#endregion
public FixedLengthReader(ErrorMode ErrorMode,int? Version)
: base(typeof(RecordType),Version)
{

emode = ErrorMode;
//check if an attribute is set
VersionAttribute
va = GetAttribute<VersionAttribute>();
if
(va != null)
{
if (version.HasValue && va.Version != version)
throw new Exception(
string.Format(
"Version was set in the constructor but also with a VersionAttribute on the recordtypes class. Versions can not be diffferent\nVersion in constructor:{0}\nVersion on {1}: {2}"

, version, Base.FullName, va.Version));
this.version = va.Version;
}
//check class level attributes
foreach(FLRAttribute fa in GetAttributes())
if (fa is VersionAttribute || ! fa.CheckVersion(version))
{
//skip this attribute: wrong version
//or in case of fa is versionattribute: already handled
}
else
if(fa is RowLengthAttribute)
{
this.RowLength = (fa as RowLengthAttribute).RowLength;
}
else
if(fa is DefaultFloatParserAttribute)
{
fparser = (fa as DefaultFloatParserAttribute).ParserType;
}
else
if(fa is DefaultParserAttribute)
{
DefaultParserAttribute dp = fa as DefaultParserAttribute;
typeparsers[dp.FieldType] = dp.ParserType;
}
else
if (fa is ReadToEndOfLineAttribute)
{
ReadToEndOfLine = (fa as ReadToEndOfLineAttribute).Enabled;
}
//floating type parsers vullen
typeparsers[typeof(double)]
= typeparsers[typeof(float)]
= typeparsers[typeof(decimal)]
= fparser;
//Velden bepalen
List
<Field> fields = new List<Field>();
Type
pat = typeof(PositionAttribute);
Field
f;
bool
needsort = false;
foreach
(FieldInfo fi in Base.GetFields())
{
FLRAttribute a = GetAttribute<FLRAttribute>(fi);
if
(a!= null)
{
if (!a.CheckVersion(version))
{
//attribute does not apply to this version
continue
;
}
if(a is ChildAttribute)
{
if(children==null)
children = new List<ChildBase>();
children.Add(ChildBase.CreateChild(fi,a as ChildAttribute));
}
else

{
f = new Field(fi,a as FLRFieldAttribute);
if
(a is FixedLengthAttribute)
{
f.Length = (a as FixedLengthAttribute).Length;
}
else
if(a is PositionAttribute)
{
PositionAttribute pa = a as PositionAttribute;
f.Position = pa.Position;
f.Length = pa.Length;
needsort = true;
}
//assign a parser if none was assigned in a previous step
if
(f.Parser==null)
{
if(typeparsers.TryGetParser(f.FieldInfo.FieldType,out f.Parser))
{
//no further action necessary , a default parser was assigned in the action above
}
else
if (f.FieldInfo.FieldType.IsEnum)
{
//enumerations get our special love and attention
typeparsers.Add(f.FieldInfo.FieldType,
typeof(EnumParser<>).MakeGenericType(f.FieldInfo.FieldType));
f.Parser = typeparsers.GetParser(f.FieldInfo.FieldType);
}
else
//unknown type or no default parser available:
//assign textparser

f.Parser = new TextParser();
}
fields.Add(f);
}
}
}
if(fields.Count==0)
throw new Exception(
string.Format(
"[{0}] is not a valid source.\nThe generic field type does not contain any position attributes. Check if the type contains position attributes. If you use versioning, please check the version numbers of the attributes (Version of this Reader instance is{1})"
,typeof(RecordType).FullName,
Version==null ? " not set" : ": " + Version
));
if(needsort)fields.Sort();
int to = fields.Count -1;
for
(int i = 0 ; i <=to; i++ )
{
f = fields[i] as Field;
if
(i < to)
{
f.Next = fields[i+1] as Field;
if
(f.Length==-1 ) f.Length = f.Next.Position - f.Position;
}
if
(f.Position==-1)
{
f.Position = i==0 ? 0 : (fields[i-1] as Field).FirstPositionNextField;
}
}
firstfield = fields[0] as Field;
HasChildren = children != null;
}


readonly
ParserCollection typeparsers = new ParserCollection();
///
<summary>
///
The default floating type parser. This parser
///
can be overriden by using the proper attributes
///
</summary>
readonly
Type fparser = typeof(FloatParser);

public readonly bool HasChildren;
public RecordType ReadLine(string Line)
{
RecordType o = getInstance();
Fill(o,Line);
return
o;
}
ConstructorInfo ci;
internal
RecordType getInstance()
{
if (ci == null)
ci = typeof(RecordType).GetConstructor(Type.EmptyTypes);
if (ci == null)
throw new Exception("To use this functionality the RecordType should contain an accessible parameterless constructor. "
+ typeof(RecordType).FullName + " does not contain such a constructor");
return (RecordType)ci.Invoke(null);
//didn't include the generic restraint 'new()' because the class should

//be usable for filling of existing objects as well

//return new RecordType() ;
}
public
RecordType ReadLine(Stream stream)
{
RecordType o = getInstance();
Fill(o,stream);
return
o;
}
public bool HasLineFeeds = true;
/// <summary>Leest een heel bestand uit en geeft een arraylist terug
///
met de gevulde instanties van het 'Base' type</summary>
///
<param name="File" type="string"></param>
///
<returns></returns>
public
List<RecordType> ReadFile(string File)
{
using(StreamReader sr = new StreamReader(File))
{
List<RecordType> al = ReadFile(sr);
sr.Close();
return
al;
}
}
public
List<RecordType> ReadFile(StreamReader sr)
{
List<RecordType> al = new List<RecordType>();
long
pos = 0;
Stream
s = sr.BaseStream;                
while
((int)( s.Length - s.Position) > firstfield.Length)
{
try
{
pos = s.Position;
al.Add(ReadLine(s));
}
catch

{
if(RollBack)s.Position = pos;
if
(SkipLine)MoveToNextLine(s);
if
(ThrowExceptions)throw;
}
}
return
al;
}
#region Errormode
bool
checkEmode(ErrorMode em)
{
return (emode & em) > 0;
}
bool
ThrowExceptions
{
get{return checkEmode(ErrorMode.Throw);}
}
bool
RollBack
{
get{return checkEmode(ErrorMode.RollBack);}
}
bool
SkipLine
{
get{return checkEmode(ErrorMode.SkipLine);}
}
#endregion
/*
public static List<RecordType> ReadFile(string File)
{
return ReadFile(File,ErrorMode.SkipLine);
}*/

public
static List<RecordType> ReadFile(string File,ErrorMode em)
{
return new FixedLengthReader<RecordType>(em).ReadFile(File);
}
int b;
public
void MoveToNextLine(Stream stream)
{
search(stream,false);
ConsumeLineFeeds(stream);
}
void search(Stream stream, bool linefeeds)
{
while(stream.Position < stream.Length)
{
b = stream.ReadByte();
if
(
(b== '\r' || b== '\n') == linefeeds
)continue;
stream.Position--;
break
;
}
}
public
void ConsumeLineFeeds(Stream stream)
{
search(stream,true);
}
public void Fill(RecordType ObjectToFill,string Line)
{
Fill(ObjectToFill,Line,null);
}
/// <summary>de algemene functie, of Line of stream wordt gebruikt, maar niet beide</summary>
///
<param name="ObjectToFill" type="object"></param>
///
<param name="Line" type="string"></param>
///
<param name="stream" type="System.IO.Stream"></param>
///
<returns></returns>
void
Fill(RecordType ObjectToFill,string Line, Stream stream)
{
Field f = firstfield;
int
b;
while
(f != null)
{
string s;
if
(f.Length==-1)
{
if(f.Next != null)
f.Length = f.Next.Position - f.Position;
else
{
if(Line != null)
f.Length = Line.Length - f.Position;
else
{
long pos = stream.Position;                            
while
((b=stream.ReadByte()) != '\r' && b != '\n'){}
f.Length =(int)( stream.Position - pos);
stream.Position = pos;
}
}
}
if
(stream==null)
s= Line.Substring(f.Position,f.Length);
else
{
byte[] buffer = new byte[f.Length];
stream.Read(buffer,0,f.Length);
s = System.Text.Encoding.ASCII.GetString(buffer);
}

f.FieldInfo.SetValue(ObjectToFill,ParseField(f,ref s));
if
(!f.Parser.Success)
{
if(ThrowExceptions)
throw new Exception(
string.Format("Value '{0}' could not be parsed in column {1} (position {2} to {3})"
,s,f.FieldInfo.Name,f.Position,f.FirstPositionNextField-1));
}
f= f.Next;
}
// onderstaande functionaliteit is alleen beschikbaar als een
//stream wordt gelezen. Als maar 1 regel ingelezen wordt -> exit

if
(stream==null)return;
//in het geval van een vaste rijlengte, de positie verder zetten
if
(EmptyLength > 0)
{
stream.Position += EmptyLength;
}
//Indien nodig, linefeeds ook lezen om de positie goed te zetten
if
(ReadToEndOfLine)
MoveToNextLine(stream);
else if(HasLineFeeds)
{
ConsumeLineFeeds(stream);
}
//Als het Base type subregels bevat, die inlezen
//dit kan alleen als via een stream ingelezen wordt

if
(HasChildren)
{
foreach(ChildBase c in children)
{
while(c.Fill(ObjectToFill,stream)){}
}
}
if(ObjectToFill is IAfterFill)
(ObjectToFill as IAfterFill).onAfterFill();
}

int emptlen=-1;
///
<summary>
///
De niet gebruikte opvulling ingeval een vaste rijlengte gebruikt wordt
///
</summary>
int
EmptyLength
{
get
{
if(emptlen==-1)
{
if(RowLength > 0)
{
emptlen = RowLength;
Field
f = firstfield;
while
(f!=null)
{
emptlen -= f.Length;
f = f.Next;
}
}
else
emptlen = 0;
}
return
emptlen;
}
}
protected virtual object ParseField(Field f,ref string s)
{    
return Convert.ChangeType(
f.Parser.Parse(ref s),
f.FieldInfo.FieldType);    
}

/// <summary>Betere methode om te vullen: rechtstreeks van een (file)stream, zodat al de 'string' stukjes overgelagen kunnen worden</summary>
///
<param name="ObjectToFill" type="object"></param>
///
<param name="stream" type="System.IO.Stream"></param>
///
<returns></returns>
public
void Fill(RecordType ObjectToFill, Stream stream)
{
Fill(ObjectToFill,null, stream);
}
}
/// <summary>
///
interface die een klasse kan implementeren om nadat de FLR klaar is met inlezen
///
custom code uit te laten voeren
///
</summary>
public
interface IAfterFill
{
void onAfterFill();
}
/// <summary>
///
Used in the <see cref="FixedLengthReader"/> to indicate how errors should be handled
///
</summary>
[Flags]
public
enum ErrorMode
{
Silent,
Throw =1,
RollBack = 2,
SkipLine = 4 | RollBack
}
#region
Attributes
///
<summary>
///
This base class but serves for recognizing
///
the FRL attributes.
///
</summary>
public
abstract class FLRAttribute:Attribute
{
#region Version
private int? version;
///
<summary>
///
Use this property if the attribute only applies to a specific version
///
NB: this does NOT set the version of the class this attribute applies to. The only
///
exception being the <see cref="VersionAttribute"/> where the Version does do exactly that
///
</summary>
public
int Version
{
get { return version.HasValue ? version.Value : 0; }
set
{ version = value; }
}
private VersionComparissons verscomp;
bool
verscompset;
/// <summary>
///
Determines how versions are checked. Only applies if a <see cref="Version"/> has
///
been set
///
</summary>
public
VersionComparissons VersionComparisson
{
get { return verscomp; }
set

{
verscomp = value;
verscompset = true;
}
}
/// <summary>
///
This value can't be set directly through the attribute (inullable members are not
///
allowed as properties in attribute setting it seems)
///
</summary>
internal
int? VersionNumber
{
get { return version; }
set
{ version = value; }
}
/// <summary>
///
Checks if the attribute corresponds with the current version.
///
If no version is applied to the <see cref="FLRAttribute"/>, it is
///
valid for all versions
///
</summary>
///
<param name="a"></param>
///
<returns></returns>
internal
bool CheckVersion(int? FLRVersion)
{
if (version == null || version==FLRVersion) return true;
if
(FLRVersion.HasValue && verscompset)
{
if (verscomp == VersionComparissons.From)
return FLRVersion.Value >= version.Value;
if (verscomp == VersionComparissons.UpUntil)
return FLRVersion.Value <= version.Value;
}
return
false;
}
#endregion
}
#region Fields (attributes to identify the columns in the file)
///
<summary>
///
De basis voor een attribuut voor de velden
///
</summary>
[AttributeUsage(AttributeTargets.Field)]
public
abstract class FLRFieldAttribute:FLRAttribute
{
Type parser;
///
<summary>
///
Geeft de mogelijkheid om een aangepast type Parser toe te kennen. Een parser zet de
///
waarde uit het bestand (tekst dus) om naar de uiteindelijke waarde.
///
Als deze niet specifiek is opgegeven, wordt gekeken naar het velddtype en geprobeerd
///
zelf de juiste Parser toe te kennen.
///

///
NB: Type moet afgeleid zijn van het type <see cref="ImportExport.Parsers.Parser"/> en de klasse moet een parameterloze
///
constructor bevatten
///
</summary>
public
Type Parser
{
get{return parser;}
set

{
ImportExport.ParserBase.CheckType(value);
parser = value;
}
}
}

/// <summary>
///
Dit attribuut kan worden gebruikt om aan te geven dat 1. een veld moet meegenomen worden
///
bij gebruik van een <see cref="FixedLengthReader"/> en 2. Hoe lang dat veld dan wel niet is.
///

///
NB: de velden moeten uiteraard wel in de goede volgorde in de klasse staan.
///
</summary>
public
class FixedLengthAttribute:FLRFieldAttribute
{
public readonly int Length;
public
FixedLengthAttribute(int Length)
{
this.Length = Length;
}
}

/// <summary>
///
Alternatief voor velden in de goede volgorde aanhouden. <see cref="FixedLengthAttribute"/>
///
heeft absoluut de voorkeur voor zijn overzichtelijkheid, maar indien die methode niet afdoende
///
is, is er ook de mogelijkheid om de positie aan te geven
///
</summary>
///
<remarks></remarks>
public
class PositionAttribute:FLRFieldAttribute
{
public readonly int Position;
public
readonly int Length;
public
PositionAttribute(int Position):this(Position,-1)
{
}
public PositionAttribute(int Position,int Length)
{
this.Position= Position;
this
.Length = Length;
}
}
#endregion

#region Childs (attributes to indicate a class field is a container that holds child rows)
///
<summary>
///
Attribute to indicate Child rows
///
</summary>
public
class ChildAttribute:FLRAttribute
{

public
readonly string StartsWith;
public
readonly int Count = 1;        
///
<summary>
///
Dit attribuut zonder argumenten geeft simpelweg aan, dat de regel onder de hoofdregel
///
(of onder een bovenliggende regel met ChildAttribute) de regel is voor het veld (toewijzen aan
///
een collectie is met deze overload natuurlijk niet mogelijk) waaraan dit attribuut is toegekend.
///
</summary>
public
ChildAttribute()
{
}
/// <summary>
///
Als dit attribuut wordt toegekend, moet een regel in het bestand beginnen met de
///
waarde opgegeven in "StartsWith" om te voldoen aan dit type en dan ingelezen te worden.
///
Dit kunnen meerdere regels onder elkaar zijn.
///

///
NB: Als StartsWith is ingevuld, gaat de Reader ervan uit dat dit een prefix is, waarna
///
welke de werkelijke gegevens beginnen. Als de positie niet vooruitgeschoven moet worden en
///
deze waarde alsnog ingelezen moet worden, zorg dan dat ConsumePrefix = false gezet wordt
///
(normaal attribuut: [Child("bla")]  , wordt dan [Child("bla", ConsumePrefix = false)]
///
</summary>
public
ChildAttribute(string StartsWith)
{
this.StartsWith = StartsWith;
}
bool consumepre = true;
public
bool ConsumePrefix
{
get{return consumepre;}
set
{consumepre = value;}
}
/// <summary>
///
Geeft aan dat de <c>Count</c> regels onder de bovenliggende regel (de hoofdregel, danwel
///
een andere subregel) horen bij deze collectie</summary>
///
<param name="Count" type="int"></param>
///
<returns></returns>
public
ChildAttribute(int Count)
{
this.Count = Count;
}
internal bool IsMatch(Stream s)
{
if(StartsWith!=null)
{
for(int i=0 ; i < StartsWith.Length ; i++)
{
if(s.ReadByte() != StartsWith[i])
{
s.Position -= ++i;
return
false;
}
}
if
(!consumepre)
s.Position -= StartsWith.Length;
}
//als tot hier gekomen is: is het een match
return
true;
}
}
#endregion
#region Class Attributes (these attributes further set properties of a FixedLengthReader)
///
<summary>
///
Basisklasse voor attributen voor een rij-klasse (een klasse die de equivalent is voor
///
een rij in een fixed-length bestand)
///
</summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]    
public
abstract class FLRClassAttribute:FLRAttribute
{
}
///
<summary>
///
Met deze waarde kan een vaste rijlengte worden aangegeven, in geval een extra lege ruimte
///
is toegevoegd achter het laatste veld.
///
</summary>    
public
class RowLengthAttribute:FLRClassAttribute
{
public readonly int RowLength = -1;
///
<summary>
///
Met deze waarde kan een vaste rijlengte worden aangegeven, in geval een extra lege ruimte
///
is toegevoegd achter het laatste veld.
///
</summary>
public
RowLengthAttribute(int RowLength)
{
this.RowLength=RowLength;
}
}
public class ReadToEndOfLineAttribute:FLRClassAttribute
{
public readonly bool Enabled;
///
<summary>
///
Door dit attribuut toe te kennen wordt aangegeven of dat na het inlezen van het laatste
///
veld, doorgelezen moet worden tot aan het begin van de volgende rij.
///
(Als het om een vaste rij lengte gaat, kan ook het <see cref="RowLengthAttribute"/> attribuut
///
gebruikt worden)
///
Standaard wordt deze methodiek gebruikt door de <see cref="FixedLengthReader"/>, maar
///
met dit attribuut kan dit worden uitgezet of vastzetten
///
</summary>
public
ReadToEndOfLineAttribute(bool Enabled)
{
this.Enabled=Enabled;
}
}
/*
/// <summary>
/// Met deze waarde kan een minimum lengte
/// </summary>    
public class MinimumRowLengthAttribute:FLRClassAttribute
{
public readonly int MinimumRowLength = -1;
/// <summary>
/// Met deze waarde kan een vaste rijlengte worden aangegeven, in geval een extra lege ruimte
/// is toegevoegd achter het laatste veld.
/// </summary>
public MinimumRowLengthAttribute(int RowLength)
{
this.MinimumRowLength=RowLength;
}
}
*/
#region Versioning
/// <summary>
///
The kind of version comparisson that is used when checking if an <see cref="FLRAttribute"/> should
///
be used in a <see cref="FixedLengthReader"/>
///
</summary>
public
enum VersionComparissons
{
/// <summary>
///
Only the specific version is used (default)
///
</summary>
EqualsTo,
///
<summary>
///
All versions equal to or greater than the specified version are used
///
</summary>
From,
///
<summary>
///
Only versions smaller than or equal to this version are allowed.
///
</summary>
UpUntil
}
///
<summary>
///
An attribute to set the version of a class. This does the same
///
as including a version in the constructor of a new instance of a fixedlenghtreader,
///
but this way you can indicate a class (eg an inherited one) this class
///
will fixate the version when a <see cref="FixedLenghtReader"/> for this
///
type of class is created.
///
NB, using this class means that the <see cref="Version"/> property points
///
to the version of the class, not to the version when this attribute should be
///
used, as it means in the other attributes
///
</summary>
public
class VersionAttribute:FLRClassAttribute
{
/// <summary>
///
Sets this class to hold a fixed version number. Newly created instances
///
<see cref="FixedLengthReader"/>s using this class will use the specified version
///
</summary>
///
<param name="Version"></param>
public
VersionAttribute(int Version)
{
this.Version = Version;
}
}
#endregion
#endregion

#region ImportExport.Parsers
public abstract class ParserBase
{
/// <summary>
///
Checks if the given type is a valid ParserBase type
///
</summary>
///
<param name="value"></param>
internal
static void CheckType(Type value)
{
if (value != null)
{
if (!typeof(ParserBase).IsAssignableFrom(value))
throw new Exception("Type must inherit from Parser");
if (value.GetConstructor(Type.EmptyTypes) == null)
throw new Exception("Type must implement a parameterless constructor");
if (value.IsAbstract)
throw new Exception("Type can not be abstract");
}
}
public object Parse(ref string value)
{
if (value[0] == ' ' || value[value.Length - 1] == ' ') value = value.Trim();
success = true;
return
Parse(ref value, ref success);
}
protected
abstract object Parse(ref string value, ref bool success);
public abstract Type TargetType { get;}
bool success;
///
<summary>
///
Indicates if the last called <see cref="Parse"/> was successful
///
</summary>
public
bool Success
{
get { return success; }
}
public static implicit operator ParserBase(Type type)
{
return GetParser(type);
}
///
<summary>
///
Returns the default parser for the specified type
///
</summary>
///
<param name="type"></param>
///
<returns></returns>
internal
static ParserBase GetParser(Type type)
{
if (type == null) return null;
return
(ParserBase)Activator.CreateInstance(
typeof(Parser<>).MakeGenericType(type));
}
}
/// <summary>
///
Basis-klasse om een waarde te parsen
///
</summary>
public
abstract class Parser<T>:ParserBase
{
protected override object Parse(ref string value, ref bool success)
{
return parse(ref value, ref success);
}
public
abstract T parse(ref string value, ref bool Success);
public override Type TargetType
{
get { return typeof(T); }
}
}
/// <summary>
///
Geeft de mogelijkheid om voor een bepaald type veld een parser toe te kennen. Dit geldt
///
dus voor alle velden van dat type, behalve als voor dat veld weer een andere parser
///
is toegekend.
///

///
Omdat floating waarde meerdere typen kan zijn en omdat deze zoveel voorkomt, bestaat daarvoor
///
een apart attribuut (<see cref="DefaultFloatParserAttribute"/>)
///
</summary>
public
class DefaultParserAttribute:FLRClassAttribute
{
public readonly Type FieldType,ParserType;
public
DefaultParserAttribute(Type FieldType, Type ParserType)
{
ParserBase.CheckType(ParserType);
this
.ParserType=ParserType;
this
.FieldType=FieldType;
}
}
/// <summary>
///
Geeft de mogelijkheid om een parser toe te kennen voor alle float - waarde velden
///
in de klasse. Per veld kan alsnog een Parser worden aangegeven, maar met behulp
///
van dit attribuut kan voor alle floats tegelijk 1 worden toegekend om herhaling te voorkomen.
///
(NB, Parsers per veld aangegeven gaan dus voor)
///
</summary>
public
class DefaultFloatParserAttribute:DefaultParserAttribute
{

///
<summary>
///
Geeft de mogelijkheid om een parser toe te kennen voor alle float - waarde velden
///
in de klasse. Per veld kan alsnog een Parser worden aangegeven, maar met behulp
///
van dit attribuut kan voor alle floats tegelijk 1 worden toegekend om herhaling te voorkomen.
///
(NB, Parsers per veld aangegeven gaan dus voor)
///
</summary>
public
DefaultFloatParserAttribute(Type ParserType):base(typeof(ValueType),ParserType)
{
}
}
/// <summary>
///
Klasse die dient om een waarde rechtstreeks tekst om te zetten. Dit doet dus inderdaad
///
niet erg veel ;-)
///
</summary>
public
class TextParser:Parser<string>
{
public override string parse(ref string value,ref bool Success)
{
return value;
}
}
/// <summary>
///
Klasse die dient om een waarde rechtstreeks naar een Integer waarde te parsen (dat is:
///
naar een niet gebroken waarde, niet perse naar het Type int.
///
</summary>
public
class IntegerParser:Parser<long>
{
public override long parse(ref string value,ref bool Success)
{
return ParseString(ref value, ref Success);
}
public static long ParseString(ref string value,ref bool Success)
{
for (int i = 0; i < value.Length; i++)
if (!char.IsDigit(value[i]))
{
Success = false;
return
0;
}
return long.Parse(value);
}
}
public class EnumParser<EnumType> : Parser<EnumType>
{

public
override EnumType parse(ref string value, ref bool Success)
{
return (EnumType)Enum.Parse(typeof(EnumType), value, true);
}
}
/// <summary>
///
Basis voor floating values parsen. Deze standaard variant gebruikt een "." voor
///
decimaal scheidingsteken en gaat uit van geen duizendtal scheidings teken
///
</summary>
public
class FloatParser:Parser<double>
{
public readonly NumberFormatInfo nfi;
public
readonly NumberStyles ns;
public
FloatParser()
{
nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = decimalSeparator();
nfi.NumberGroupSeparator = thousandSeparator();
if(nfi.NumberDecimalSeparator.Length>0)
ns |= NumberStyles.AllowDecimalPoint;
if(nfi.NumberGroupSeparator.Length>0)
ns |= NumberStyles.AllowThousands;
}
public virtual string decimalSeparator()
{
return ".";
}
public virtual string thousandSeparator()
{
return "";
}
public override double parse(ref string value, ref bool Success)
{
double d;
Success = double.TryParse(
value,
ns,
nfi
,out d);
return d;
}
}
/// <summary>
///
Deze floatparser gebruikt de instellingen van de actieve cultuur om de waarde
///
om te zetten
///
</summary>    
public
class FloatCultureParser:FloatParser
{
public override string decimalSeparator()
{
return CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
}
public override string thousandSeparator()
{
return CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
}


}

/// <summary>
///
Standaard functionaliteit als een gebroken waarde als gehele waarde wordt weggeschreven of
///
als zodanig moet worden behandeld en daarna door 10^x gedeeld moet worden
///
</summary>
public
class FloatDivideParser:Parser<double>
{
public FloatDivideParser(int DecimalCount)
{
this.DecimalCount = DecimalCount;
}
///
<summary>
///
Het aantal decimalen waardoor de integer waardes gedeeld moet worden
///
</summary>
public
readonly int DecimalCount;
public override double parse(ref string value, ref bool Success)
{
value = value.Replace(",",null).Replace(".",null);
double
d = (double)IntegerParser.ParseString(ref value,ref Success);
if
(Success)d/= Math.Pow(10,DecimalCount);
return
d;
}
}
public class FloatDivide100:FloatDivideParser
{
public FloatDivide100() : base(2) { }
}
public class FloatDivide1000:FloatDivideParser
{
public FloatDivide1000() : base(3) { }
}
/// <summary>
///
Probeert een datetime te parsen met de huidige cultuur instellingen.
///
NB: dit is wel de standaard voor een cultuur, maar niet de standaard van de
///
<see cref="FixedLengthReader"/>. De FLR gebruikt standaard voor DateTime velden
///
de <see cref="DateTimeExactParser"/>
///
</summary>
public
class DateTimeParser:Parser<DateTime>
{
public override DateTime parse(ref string value, ref bool Success)
{
DateTime dt;
try

{
dt =DateTime.Parse(value);
}
catch

{
dt = DateTime.MinValue;
Success = false;
}
return
dt;            
}
}
/// <summary>
///
Probeert een waarde om te zetten vanuit een vast formaat. Afgeleide klassen kunnen
///
dit formaat overschrijven.
///
Voor deze klasse is dat formaat: "yyyyMMdd"
///
</summary>
public
class DateTimeExactParser:Parser<DateTime>
{
string format;
public
DateTimeExactParser()
{
format = GetFormat();
}
public
override DateTime parse(ref string value, ref bool Success)
{
try
{
return DateTime.ParseExact(value,format,null);
}
catch

{
Success = false;
return
DateTime.MinValue;
}
}
/// <summary>
///
Formaat waarmee getracht wordt de string te parsen
///
</summary>
///
<returns></returns>
protected
virtual string GetFormat()
{
return "yyyyMMdd";
}
}

/// <summary>
///
If the value contains "1" or "true", the text is considered true, otherwise false
///
</summary>
public
class BoolParser : Parser<bool>
{
public override bool parse(ref string value, ref bool Success)
{
if (value == null) return false;
switch
(value.Length)
{
case 1:
return value == "1";
case 4:
return value.ToLower() == "true";
default:
return false;
}
}
}
/// <summary>
///