Skip to content

Instantly share code, notes, and snippets.

@FennyFatal
Last active August 29, 2015 14:10
Show Gist options
  • Save FennyFatal/decec88912e89c4521eb to your computer and use it in GitHub Desktop.
Save FennyFatal/decec88912e89c4521eb to your computer and use it in GitHub Desktop.
Quick tool to build a node tree for a message in the HL7 standard.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace HL7Visualizer
{
public class HL7
{
string[] rawMessage;
//These are the default values, we'll load them from the MSH line if there is one.
static char escape = '\\';
static string delimiters = "|~^&";
//These are characters that must be escaped to match them literally.
const string EscapeChars = @".$^{[(|)*+?\";
/*
* These delimiters can be custom defined based on the needs of the HL7 user.
* Example: MSH|^~\&
* | is the field split
* ^ splits subfields
* ~ repeats a field
* \ is the escape char.
* & repeats a subfield.
* Order of operations would be: |~^&
* Finally we will remove each escape char immediately after the escaped delimiter has been processed.
* */
private void parseMSH(string line)
{
delimiters = String.Empty + line[3] + line[5] + line[4] + line[7];
escape = line[6];
}
public HL7()
{
rawMessage = null;
}
public HL7(string[] lines)
{
rawMessage = lines;
foreach (string line in rawMessage)
{
// If we have a MSH line:
if (line.StartsWith("MSH"))
{
parseMSH(line);
}
}
}
public HL7(string lines)
{
rawMessage = lines.Split('\x0D');
foreach (string line in rawMessage)
{
// If we have a MSH line:
if (line.StartsWith("MSH"))
{
parseMSH(line);
}
}
}
public static void PopulateNodes(TreeNodeCollection nodes, string[] lines)
{
new HL7(lines).PopulateNodes(nodes);
}
public static void PopulateNodes(TreeNodeCollection nodes, string lines)
{
new HL7(lines).PopulateNodes(nodes);
}
public void PopulateNodes(TreeNodeCollection nodes)
{
nodes.Clear();
foreach (string m in rawMessage)
{
if (!m.Equals(String.Empty))
//This function will recursively build our node tree.
nodes.Add(ParseChunks(-1, m, delimiters));
}
}
private TreeNode ParseChunks(int index, string line, string delimiters)
{
// If we are passed this line number we don't attach an index to the message.
bool rootnode = index == -1;
// Define our return value.
TreeNode retval = null;
// This is the last Parse.
if (delimiters.Length > 0)
{
//Get the next delimiter
char delimiter = delimiters[0];
//Consume a delimiter if there is only one set the string to empty.
if (delimiters.Length > 1)
delimiters = delimiters.Substring(1);
else
delimiters = String.Empty;
//Build the treenode with the contents of the line we were passed.
retval = new TreeNode((rootnode ? String.Empty : index.ToString() + " - ") + line);
if (rootnode && line.StartsWith("MSH"))
line = line.Replace("MSH|", @"MSH|\||");
//Define the regex and escape our delimiters or escape character if required.
//We define a lookbefore for splitting when it is NOT our escape char: (?<!EscapeChar)DelimiterChar
string regex = @"(?<!" + (EscapeChars.Contains(escape) ? "\\" + escape : String.Empty + escape) + @")" + (EscapeChars.Contains(delimiter) ? "\\" + delimiter : String.Empty + delimiter);
//Split with a lookaround to make sure we trust escape chars.
string[] chunks = Regex.Split(line, regex);
//If there were no delimiters in the line there will be only one item in this array.
if (chunks.Length > 1)
{
for (int i = 0; i < chunks.Length; i++)
{
//Replace any escaped delimiters there may be. (We could do this in a seperate loop, but this is fewer cycles traded for duplication of code.)
chunks[i] = chunks[i].Replace(String.Empty + escape + delimiter, String.Empty + delimiter);
//Don't bother if we have an empty segment
if (!chunks[i].Equals(String.Empty))
{
//Recursively call with the new set of the delimiter queue.
retval.Nodes.Add(ParseChunks(rootnode ? i : i + 1, chunks[i], delimiters));
}
}
}
else //We didn't have a match this time, but there are more delimiters to parse.
{
//Replace any escaped delimiters there may be.
line = line.Replace(String.Empty + escape + delimiter, String.Empty + delimiter);
//Recursive call - We should return the next node so that we don't repeat ourselves.
retval = ParseChunks(index, line, delimiters);
}
}
else //No more delimiters, just return what we have.
{
retval = new TreeNode(index.ToString() + " - " + line);
}
return retval;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment