geekswithblogs.net
HOME
ARCHIVES
CONTACT
LOGIN
El Grego
BizTalk blog
News
My Stats
Posts - 26
Comments - 24
Trackbacks - 55
Twitter
gvdwiele
Error: 18456, severity 14, state 16...digging
about 47 days ago
gvdwiele
Configuring a BizTalk WCF Static Port to Behave as a Dynamic Port WITH NACKS. Aint easy.
about 146 days ago
gvdwiele
Grandpa is debugging with Stan Getz.
about 158 days ago
gvdwiele
looking into Ivy, the dependency manager ;-)
about 159 days ago
gvdwiele
watching the $ fall
about 230 days ago
gvdwiele
refactoring to patterns...or how one 500-line cs file can turn into 50 classes of 20 lines.
about 251 days ago
gvdwiele
refactoring to patterns...or how one 500 cs file can turn into 50 classes of 20 lines.
about 251 days ago
gvdwiele
I'm selling my Apple stock.
about 255 days ago
Recent Comments
Thank you for posting this. It helped me a lot. I ...
by Marcus Nyberg
I'm fully trained and good at throwing things out....
by Grego
What FFASM bug?!!! You cannot just throw that out ...
by Mikael Sand
No bother. Glad it was of use. Your audit trail ap...
by Grego
My fault i forgot about the second hoop, I added t...
by Patrick Wellink
Recent Posts
CharacterTranscoder pipeline component on github.com
Question marks in your flatfile output CONTINUED
Question marks in your flatfile output?
BizTalk Arbitrary Binary Email Attachments (or how to fix unreadable attachments)
BAM Typed API alternative
Archives
September, 2009 (1)
August, 2009 (2)
July, 2009 (1)
June, 2009 (2)
May, 2009 (2)
February, 2009 (1)
December, 2008 (1)
September, 2005 (2)
April, 2005 (1)
March, 2005 (2)
February, 2005 (1)
November, 2004 (4)
October, 2004 (2)
September, 2004 (4)
Post Categories
BizTalk - EAI - B2B
<< Some thoughts on Service Location (SL) and Dependency Injection (DI) with BizTalk...
|
Home
|
BAM Portal Related Document Links That Actually Work >>
Custom StreamReader with FSM
This post is about Finite State Machines, the
Flying Spaghetti Monster
is for next time.
For one of the EAI projects I'm working on I needed a specific StreamReader that adds field-wrappers (around the fields, obviously) of a char-based stream. For example:
field1-1,field1-2;field2-1,field2-2 => "field1-1","field1-2";"field2-1","field2-2"
My first approach was the currently popular test-driven one: a default passthru read method and a bunch of tests, that of-course all initially failed. I trusted my creativeness in order to randomly identify all different types of input-string combinations:
NoFieldSeperator
NoRecordSeperator
EmptyString
FieldWithSingleWrapCharInfix
FieldWithSingleWrapCharPostfix
etc...
Inside the reader I added different if-then-else clauses to manipulate the output, until a given test succeeded. Every newly added if-clause of course potentially broke 1 or more of the previous tests. One can imagine it took quite a lot of time and way too many iterations to get this very simple parser working!
After a while I was lucky enough to get all cases working: hurray! In fact, not really :-(
I didn't have complete confidence in what I had just built and how it was built. This wouldn't work for more complex scenarios.
How could I ever be sure that I didn't forget some special case? I needed a more formal approach...
So I modeled the reader as a finite state machine, thereby defining the actions i.e. the desired output character(s) for every input character depending on the state. It's very easy, just start drawing on a piece of paper and think about the states and the output for every input.
The reader can be in the 2 following states: 'inside a field' or 'outside a field'.
I have defined 'special' input tokens that require special handling (different action) or trigger a state transition:
RD: record delimiter
FD: field delimiter
WS: whitespace
EoF: end of file
WC: wrap character
C: any other char
The output (or action) for a given input greatly depends upon the state the reader is in and if the input character triggers a transition. EoF, field- and record-delimiters trigger a state transition from insidefield to outsidefield.
I didn't put the actions on the diagram.
For every 1 char in the input, the reader can output 0, 1 or 2 chars.
If the input contains the field-wrap character inside a field, it should be doubled.
This requires the ability to cache a few characters.
field1,fiel"d2; => "field1","fiel""d2"
Whitespaces could be configured to be skipped if non-significant (trimming).
Also RD or FD when 'OutsideField' could be configured to generate empty fields.
For this implementation I choose the object-oriented state design pattern.
internal abstract class StreamState
{
public abstract int CharInput(WrappingFsmStreamReader context, int character);
public abstract int WhitespaceInput(WrappingFsmStreamReader context);
public abstract int FieldDelimiterInput(WrappingFsmStreamReader context);
public abstract int RecordDelimiterInput(WrappingFsmStreamReader context);
public abstract int WrapInput(WrappingFsmStreamReader context);
public abstract int EoFInput(WrappingFsmStreamReader context);
}
Here is a
sample
.
So next time you need a custom StreamReader it makes sense to model it as a FSM. Define and document the states and behavior, use this inside knowledge to write the unit tests.
posted @ Monday, May 25, 2009 9:55 AM | Filed Under [
BizTalk - EAI - B2B
]
Comments
No comments posted yet.
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification:
Remember Me?