/// <summary> Populates the given Element with data from the given Segment, by inserting /// Elements corresponding to the Segment's fields, their components, etc. Returns /// true if there is at least one data value in the segment. /// </summary> public virtual bool encode(Segment segmentObject, System.Xml.XmlElement segmentElement) { bool hasValue = false; int n = segmentObject.numFields(); for (int i = 1; i <= n; i++) { System.String name = makeElementName(segmentObject, i); Type[] reps = segmentObject.getField(i); for (int j = 0; j < reps.Length; j++) { System.Xml.XmlElement newNode = segmentElement.OwnerDocument.CreateElement(name); bool componentHasValue = encode(reps[j], newNode); if (componentHasValue) { try { segmentElement.AppendChild(newNode); } catch (System.Exception e) { throw new NuGenHL7Exception("DOMException encoding Segment: ", NuGenHL7Exception.APPLICATION_INTERNAL_ERROR, e); } hasValue = true; } } } return(hasValue); }
/// <summary> Formats a Message object into an HL7 message string using this parser's /// default encoding ("VB"). /// </summary> /// <throws> HL7Exception if the data fields in the message do not permit encoding </throws> /// <summary> (e.g. required fields are null) /// </summary> protected internal override System.String doEncode(Message source) { //get encoding characters ... Segment msh = (Segment)source.get_Renamed("MSH"); System.String fieldSepString = Terser.get_Renamed(msh, 1, 0, 1, 1); if (fieldSepString == null) { throw new NuGenHL7Exception("Can't encode message: MSH-1 (field separator) is missing"); } char fieldSep = '|'; if (fieldSepString != null && fieldSepString.Length > 0) { fieldSep = fieldSepString[0]; } System.String encCharString = Terser.get_Renamed(msh, 2, 0, 1, 1); if (encCharString == null) { throw new NuGenHL7Exception("Can't encode message: MSH-2 (encoding characters) is missing"); } if (encCharString.Length != 4) { throw new NuGenHL7Exception("Encoding characters '" + encCharString + "' invalid -- must be 4 characters", NuGenHL7Exception.DATA_TYPE_ERROR); } NuGenEncodingCharacters en = new NuGenEncodingCharacters(fieldSep, encCharString); //pass down to group encoding method which will operate recursively on children ... return(encode((Group)source, en)); }
/// <summary> Populates the given Segment object with data from the given XML Element.</summary> /// <throws> HL7Exception if the XML Element does not have the correct name and structure </throws> /// <summary> for the given Segment, or if there is an error while setting individual field values. /// </summary> public virtual void parse(Segment segmentObject, System.Xml.XmlElement segmentElement) { SupportClass.HashSetSupport done = new SupportClass.HashSetSupport(); System.Xml.XmlNodeList all = segmentElement.ChildNodes; for (int i = 0; i < all.Count; i++) { System.String elementName = all.Item(i).Name; if (System.Convert.ToInt16(all.Item(i).NodeType) == (short)System.Xml.XmlNodeType.Element && !done.Contains(elementName)) { done.Add(elementName); int index = elementName.IndexOf('.'); if (index >= 0 && elementName.Length > index) { //properly formatted element System.String fieldNumString = elementName.Substring(index + 1); int fieldNum = System.Int32.Parse(fieldNumString); parseReps(segmentObject, segmentElement, elementName, fieldNum); } else { } } } //set data type of OBX-5 if (segmentObject.GetType().FullName.IndexOf("OBX") >= 0) { Varies.fixOBX5(segmentObject, Factory); } }
/// <summary> Fills in the details of an Application Reject message, including response and /// error codes, and a text error message. This is the method to override if you want /// to respond differently. /// </summary> public virtual void fillDetails(Message ack) { try { //populate MSA and ERR with generic error ... Segment msa = (Segment)ack.get_Renamed("MSA"); Terser.set_Renamed(msa, 1, 0, 1, 1, "AR"); Terser.set_Renamed(msa, 3, 0, 1, 1, "No appropriate destination could be found to which this message could be routed."); //this is max length //populate ERR segment if it exists (may not depending on version) Structure s = ack.get_Renamed("ERR"); if (s != null) { Segment err = (Segment)s; Terser.set_Renamed(err, 1, 0, 4, 1, "207"); Terser.set_Renamed(err, 1, 0, 4, 2, "Application Internal Error"); Terser.set_Renamed(err, 1, 0, 4, 3, "HL70357"); } } catch (System.Exception e) { throw new NuGenApplicationException("Error trying to create Application Reject message: " + e.Message); } }
private static NuGenTransportable makeAcceptAck(NuGenTransportable theMessage, System.String theAckCode, int theErrorCode, System.String theDescription) { Segment header = ourParser.getCriticalResponseData(theMessage.Message); Message out_Renamed; try { out_Renamed = DefaultApplication.makeACK(header); } catch (System.IO.IOException e) { throw new NuGenHL7Exception(e); } Terser t = new Terser(out_Renamed); t.set_Renamed("/MSA-1", theAckCode); //TODO: when 2.5 is available, use 2.5 fields for remaining problems if (theErrorCode != NuGenHL7Exception.MESSAGE_ACCEPTED) { t.set_Renamed("/MSA-3", theDescription.Substring(0, (System.Math.Min(80, theDescription.Length)) - (0))); t.set_Renamed("/ERR-1-4-1", System.Convert.ToString(theErrorCode)); t.set_Renamed("/ERR-1-4-3", "HL70357"); } System.String originalEncoding = ourParser.getEncoding(theMessage.Message); System.String ackText = ourParser.encode(out_Renamed, originalEncoding); return(new NuGenTransportableImpl(ackText)); }
private void parseReps(Segment segmentObject, System.Xml.XmlElement segmentElement, System.String fieldName, int fieldNum) { System.Xml.XmlNodeList reps = segmentElement.GetElementsByTagName(fieldName); for (int i = 0; i < reps.Count; i++) { parse(segmentObject.getField(fieldNum, i), (System.Xml.XmlElement)reps.Item(i)); } }
/// <summary> <p>Returns a minimal amount of data from a message string, including only the /// data needed to send a response to the remote system. This includes the /// following fields: /// <ul><li>field separator</li> /// <li>encoding characters</li> /// <li>processing ID</li> /// <li>message control ID</li></ul> /// This method is intended for use when there is an error parsing a message, /// (so the Message object is unavailable) but an error message must be sent /// back to the remote system including some of the information in the inbound /// message. This method parses only that required information, hopefully /// avoiding the condition that caused the original error. The other /// fields in the returned MSH segment are empty.</p> /// </summary> public override Segment getCriticalResponseData(System.String message) { //try to get MSH segment int locStartMSH = message.IndexOf("MSH"); if (locStartMSH < 0) { throw new NuGenHL7Exception("Couldn't find MSH segment in message: " + message, NuGenHL7Exception.SEGMENT_SEQUENCE_ERROR); } int locEndMSH = message.IndexOf('\r', locStartMSH + 1); if (locEndMSH < 0) { locEndMSH = message.Length; } System.String mshString = message.Substring(locStartMSH, (locEndMSH) - (locStartMSH)); //find out what the field separator is char fieldSep = mshString[3]; //get field array System.String[] fields = split(mshString, System.Convert.ToString(fieldSep)); Segment msh = null; try { //parse required fields System.String encChars = fields[1]; char compSep = encChars[0]; System.String messControlID = fields[9]; System.String[] procIDComps = split(fields[10], System.Convert.ToString(compSep)); //fill MSH segment System.String version = "2.4"; //default try { version = this.getVersion(message); } catch (System.Exception) { /* use the default */ } msh = NuGenParser.makeControlMSH(version, Factory); Terser.set_Renamed(msh, 1, 0, 1, 1, System.Convert.ToString(fieldSep)); Terser.set_Renamed(msh, 2, 0, 1, 1, encChars); Terser.set_Renamed(msh, 10, 0, 1, 1, messControlID); Terser.set_Renamed(msh, 11, 0, 1, 1, procIDComps[0]); Terser.set_Renamed(msh, 12, 0, 1, 1, version); } catch (System.Exception e) { throw new NuGenHL7Exception("Can't parse critical fields from MSH segment (" + e.GetType().FullName + ": " + e.Message + "): " + mshString, NuGenHL7Exception.REQUIRED_FIELD_MISSING, e); } return(msh); }
/// <summary> Logs the given exception and creates an error message to send to the /// remote system. /// /// </summary> /// <param name="encoding">The encoding for the error message. If <code>null</code>, uses default encoding /// </param> public static System.String logAndMakeErrorMessage(System.Exception e, Segment inHeader, Parser p, System.String encoding) { // create error message ... System.String errorMessage = null; try { Message out_Renamed = NuGenDefaultApplication.makeACK(inHeader); Terser t = new Terser(out_Renamed); //copy required data from incoming message ... try { t.set_Renamed("/MSH-10", MessageIDGenerator.Instance.NewID); } catch (System.IO.IOException ioe) { throw new NuGenHL7Exception("Problem creating error message ID: " + ioe.Message); } //populate MSA ... t.set_Renamed("/MSA-1", "AE"); //should this come from HL7Exception constructor? t.set_Renamed("/MSA-2", Terser.get_Renamed(inHeader, 10, 0, 1, 1)); System.String excepMessage = e.Message; if (excepMessage != null) { t.set_Renamed("/MSA-3", excepMessage.Substring(0, (System.Math.Min(80, excepMessage.Length)) - (0))); } /* Some earlier ACKs don't have ERRs, but I think we'll change this within HAPI * so that there is a single ACK for each version (with an ERR). */ //see if it's an HL7Exception (so we can get specific information) ... if (e.GetType().Equals(typeof(NuGenHL7Exception))) { Segment err = (Segment)out_Renamed.get_Renamed("ERR"); ((NuGenHL7Exception)e).populate(err); } else { t.set_Renamed("/ERR-1-4-1", "207"); t.set_Renamed("/ERR-1-4-2", "Application Internal Error"); t.set_Renamed("/ERR-1-4-3", "HL70357"); } if (encoding != null) { errorMessage = p.encode(out_Renamed, encoding); } else { errorMessage = p.encode(out_Renamed); } } catch (System.IO.IOException ioe) { throw new NuGenHL7Exception("IOException creating error response message: " + ioe.Message, NuGenHL7Exception.APPLICATION_INTERNAL_ERROR); } return(errorMessage); }
/// <summary> Creates an ACK message with the minimum required information from an inbound message. /// Optional fields can be filled in afterwards, before the message is returned. Pleaase /// note that MSH-10, the outbound message control ID, is also set using the class /// <code>Genetibase.NuGenHL7.util.MessageIDGenerator</code>. Also note that the ACK messages returned /// is the same version as the version stated in the inbound MSH if there is a generic ACK for that /// version, otherwise a version 2.4 ACK is returned. MSA-1 is set to AA by default. /// /// </summary> /// <param name="inboundHeader">the MSH segment if the inbound message /// </param> /// <throws> IOException if there is a problem reading or writing the message ID file </throws> /// <throws> DataTypeException if there is a problem setting ACK values </throws> public static Message makeACK(Segment inboundHeader) { if (!inboundHeader.getName().Equals("MSH")) { throw new NuGenHL7Exception("Need an MSH segment to create a response ACK (got " + inboundHeader.getName() + ")"); } //make ACK of correct version System.String version = null; try { version = Terser.get_Renamed(inboundHeader, 12, 0, 1, 1); } catch (NuGenHL7Exception) { /* proceed with null */ } if (version == null) { version = "2.4"; } System.String ackClassName = SourceGenerator.getVersionPackageName(version) + "message.ACK"; Message out_Renamed = null; try { System.Type ackClass = System.Type.GetType(ackClassName); out_Renamed = (Message)System.Activator.CreateInstance(ackClass); } catch (System.Exception e) { throw new NuGenHL7Exception("Can't instantiate ACK of class " + ackClassName + ": " + e.GetType().FullName); } Terser terser = new Terser(out_Renamed); //populate outbound MSH using data from inbound message ... Segment outHeader = (Segment)out_Renamed.get_Renamed("MSH"); fillResponseHeader(inboundHeader, outHeader); terser.set_Renamed("/MSH-9", "ACK"); terser.set_Renamed("/MSH-12", version); terser.set_Renamed("/MSA-1", "AA"); terser.set_Renamed("/MSA-2", Genetibase.NuGenHL7.util.NuGenTerser.get_Renamed(inboundHeader, 10, 0, 1, 1)); return(out_Renamed); }
public static System.String encode(Segment source, NuGenEncodingCharacters encodingChars) { System.Text.StringBuilder result = new System.Text.StringBuilder(); result.Append(source.getName()); result.Append(encodingChars.FieldSeparator); //start at field 2 for MSH segment because field 1 is the field delimiter int startAt = 1; if (isDelimDefSegment(source.getName())) { startAt = 2; } //loop through fields; for every field delimit any repetitions and add field delimiter after ... int numFields = source.numFields(); for (int i = startAt; i <= numFields; i++) { try { Type[] reps = source.getField(i); for (int j = 0; j < reps.Length; j++) { System.String fieldText = encode(reps[j], encodingChars); //if this is MSH-2, then it shouldn't be escaped, so unescape it again if (isDelimDefSegment(source.getName()) && i == 2) { fieldText = NuGenEscape.unescape(fieldText, encodingChars); } result.Append(fieldText); if (j < reps.Length - 1) { result.Append(encodingChars.RepetitionSeparator); } } } catch (NuGenHL7Exception) { } result.Append(encodingChars.FieldSeparator); } //strip trailing delimiters ... return(stripExtraDelimiters(result.ToString(), encodingChars.FieldSeparator)); }
private void parse(SupportClass.Tokenizer tok, Message message, StructRef root, NuGenEncodingCharacters ec) { Terser t = new Terser(message); lock (root) { StructRef ref_Renamed = root.getSuccessor("MSH"); int field = 0; Segment segment = null; int[] fields = new int[0]; while (tok.HasMoreTokens()) { System.String token = tok.NextToken(); if (token[0] == ec.FieldSeparator) { field++; } else if (token[0] == ourSegmentSeparator) { field = 0; } else if (field == 0) { StructRef newref = drill(ref_Renamed, token); if (newref == null) { segment = null; fields = new int[0]; } else { ref_Renamed = newref; segment = t.getSegment(ref_Renamed.FullPath); fields = ref_Renamed.Fields; } } else if (segment != null && System.Array.BinarySearch(fields, (System.Object)field) >= 0) { parse(token, segment, field, ec); } } root.reset(); } }
/// <summary> <p>Returns a minimal amount of data from a message string, including only the /// data needed to send a response to the remote system. This includes the /// following fields: /// <ul><li>field separator</li> /// <li>encoding characters</li> /// <li>processing ID</li> /// <li>message control ID</li></ul> /// This method is intended for use when there is an error parsing a message, /// (so the Message object is unavailable) but an error message must be sent /// back to the remote system including some of the information in the inbound /// message. This method parses only that required information, hopefully /// avoiding the condition that caused the original error.</p> /// </summary> public override Segment getCriticalResponseData(System.String message) { System.String version = getVersion(message); Segment criticalData = NuGenParser.makeControlMSH(version, Factory); Terser.set_Renamed(criticalData, 1, 0, 1, 1, parseLeaf(message, "MSH.1", 0)); Terser.set_Renamed(criticalData, 2, 0, 1, 1, parseLeaf(message, "MSH.2", 0)); Terser.set_Renamed(criticalData, 10, 0, 1, 1, parseLeaf(message, "MSH.10", 0)); System.String procID = parseLeaf(message, "MSH.11", 0); if (procID == null || procID.Length == 0) { procID = parseLeaf(message, "PT.1", message.IndexOf("MSH.11")); //this field is a composite in later versions } Terser.set_Renamed(criticalData, 11, 0, 1, 1, procID); return(criticalData); }
/// <summary> Ensures that there is an empty repetition of the underlying message Structure. </summary> /// <returns>s true if successful, false if structure is non-repeating and full. /// </returns> private bool prepEmptyInstance() { bool success = false; if (this.currSegment == null || (this.repeating && this.currSegmentFull)) { int numExisting = parent.getAll(this.name).Length; this.currSegment = (Segment)parent.get_Renamed(this.name, numExisting); //get next rep this.currSegmentFull = false; success = true; } else if (!this.currSegmentFull) { success = true; } return(success); }
/// <summary> Creates a version-specific MSH object and returns it as a version-independent /// MSH interface. /// throws HL7Exception if there is a problem, e.g. invalid version, code not available /// for given version. /// </summary> public static Segment makeControlMSH(System.String version, NuGenModelClassFactory factory) { Segment msh = null; try { Message dummy = (Message)GenericMessage.getGenericMessageClass(version).GetConstructor(new System.Type[] { typeof(NuGenModelClassFactory) }).Invoke(new System.Object[] { factory }); System.Type[] constructorParamTypes = new System.Type[] { typeof(Group), typeof(NuGenModelClassFactory) }; System.Object[] constructorParamArgs = new System.Object[] { dummy, factory }; System.Type c = factory.getSegmentClass("MSH", version); System.Reflection.ConstructorInfo constructor = c.GetConstructor(constructorParamTypes); msh = (Segment)constructor.Invoke(constructorParamArgs); } catch (System.Exception e) { throw new NuGenHL7Exception("Couldn't create MSH for version " + version + " (does your classpath include this version?) ... ", NuGenHL7Exception.APPLICATION_INTERNAL_ERROR, e); } return(msh); }
/// <seealso cref="Genetibase.NuGenHL7.parser.Parser.doParse(java.lang.String, java.lang.String)"> /// </seealso> protected internal override Message doParse(System.String message, System.String version) { Message result = null; char fieldSep = message[3]; NuGenEncodingCharacters ec = new NuGenEncodingCharacters(fieldSep, message.Substring(4, (8) - (4))); SupportClass.Tokenizer tok = new SupportClass.Tokenizer(message.Substring(4), System.Convert.ToString(new char[] { fieldSep, ourSegmentSeparator }), true); System.String[] mshFields = getMSHFields(tok, fieldSep); System.Object[] structure = getStructure(mshFields[8], ec.ComponentSeparator); StructRef root = (StructRef)myEventGuideMap[structure[0]]; if (root == null) { result = myPipeParser.parse(message); } else { int csIndex = mshFields[11].IndexOf((System.Char)ec.ComponentSeparator); result = instantiateMessage((System.String)structure[1], version, ((System.Boolean)structure[2])); StructRef mshRef = null; lock (root) { mshRef = root.getSuccessor("MSH"); root.reset(); } Segment msh = (Segment)result.get_Renamed("MSH"); for (int i = 0; i < mshRef.Fields.Length; i++) { int fieldNum = mshRef.Fields[i]; parse(mshFields[fieldNum - 1], msh, fieldNum, ec); } parse(tok, result, root, ec); } return(result); }
/// <summary> Populates certain required fields in a response message header, using /// information from the corresponding inbound message. The current time is /// used for the message time field, and <code>MessageIDGenerator</code> is /// used to create a unique message ID. Version and message type fields are /// not populated. /// </summary> public static void fillResponseHeader(Segment inbound, Segment outbound) { if (!inbound.getName().Equals("MSH") || !outbound.getName().Equals("MSH")) { throw new NuGenHL7Exception("Need MSH segments. Got " + inbound.getName() + " and " + outbound.getName()); } //get MSH data from incoming message ... System.String encChars = Terser.get_Renamed(inbound, 2, 0, 1, 1); System.String fieldSep = Terser.get_Renamed(inbound, 1, 0, 1, 1); System.String procID = Terser.get_Renamed(inbound, 11, 0, 1, 1); //populate outbound MSH using data from inbound message ... Terser.set_Renamed(outbound, 2, 0, 1, 1, encChars); Terser.set_Renamed(outbound, 1, 0, 1, 1, fieldSep); System.Globalization.GregorianCalendar now = new System.Globalization.GregorianCalendar(); SupportClass.CalendarManager.manager.SetDateTime(now, System.DateTime.Now); Terser.set_Renamed(outbound, 7, 0, 1, 1, CommonTS.toHl7TSFormat(now)); Terser.set_Renamed(outbound, 10, 0, 1, 1, MessageIDGenerator.Instance.NewID); Terser.set_Renamed(outbound, 11, 0, 1, 1, procID); }
/// <summary> Populates the given error segment with information from this Exception.</summary> public virtual void populate(Segment errorSegment) { //make sure it's an ERR if (!errorSegment.getName().Equals("ERR")) { throw new NuGenHL7Exception("Can only populate an ERR segment with an exception -- got: " + errorSegment.GetType().FullName); } int rep = errorSegment.getField(1).Length; //append after existing reps if (this.SegmentName != null) { Terser.set_Renamed(errorSegment, 1, rep, 1, 1, this.SegmentName); } if (this.SegmentRepetition >= 0) { Terser.set_Renamed(errorSegment, 1, rep, 2, 1, System.Convert.ToString(this.SegmentRepetition)); } if (this.FieldPosition >= 0) { Terser.set_Renamed(errorSegment, 1, rep, 3, 1, System.Convert.ToString(this.FieldPosition)); } Terser.set_Renamed(errorSegment, 1, rep, 4, 1, System.Convert.ToString(this.errCode)); Terser.set_Renamed(errorSegment, 1, rep, 4, 3, "hl70357"); Terser.set_Renamed(errorSegment, 1, rep, 4, 5, this.Message); //try to get error condition text try { System.String desc = NuGenTableRepository.Instance.getDescription(357, System.Convert.ToString(this.errCode)); Terser.set_Renamed(errorSegment, 1, rep, 4, 2, desc); } catch (NuGenLookupException) { } }
private void parse(System.String field, Segment segment, int num, NuGenEncodingCharacters ec) { if (field != null) { int rep = 0; int component = 1; int subcomponent = 1; Type type = segment.getField(num, rep); System.String delim = System.Convert.ToString(new char[] { ec.RepetitionSeparator, ec.ComponentSeparator, ec.SubcomponentSeparator }); for (SupportClass.Tokenizer tok = new SupportClass.Tokenizer(field, delim, true); tok.HasMoreTokens();) { System.String token = tok.NextToken(); char c = token[0]; if (c == ec.RepetitionSeparator) { rep++; component = 1; subcomponent = 1; type = segment.getField(num, rep); } else if (c == ec.ComponentSeparator) { component++; subcomponent = 1; } else if (c == ec.SubcomponentSeparator) { subcomponent++; } else { Primitive p = Terser.getPrimitive(type, component, subcomponent); p.Value = token; } } } }
/// <summary> Populates the given Element with data from the given Segment, by inserting /// Elements corresponding to the Segment's fields, their components, etc. Returns /// true if there is at least one data value in the segment. /// </summary> public virtual bool encode(Segment segmentObject, System.Xml.XmlElement segmentElement) { bool hasValue = false; int n = segmentObject.numFields(); for (int i = 1; i <= n; i++) { System.String name = makeElementName(segmentObject, i); Type[] reps = segmentObject.getField(i); for (int j = 0; j < reps.Length; j++) { System.Xml.XmlElement newNode = segmentElement.OwnerDocument.CreateElement(name); bool componentHasValue = encode(reps[j], newNode); if (componentHasValue) { try { segmentElement.AppendChild(newNode); } catch (System.Exception e) { throw new NuGenHL7Exception("DOMException encoding Segment: ", NuGenHL7Exception.APPLICATION_INTERNAL_ERROR, e); } hasValue = true; } } } return hasValue; }
/// <summary> Populates the given Segment object with data from the given XML Element.</summary> /// <throws> HL7Exception if the XML Element does not have the correct name and structure </throws> /// <summary> for the given Segment, or if there is an error while setting individual field values. /// </summary> public virtual void parse(Segment segmentObject, System.Xml.XmlElement segmentElement) { SupportClass.HashSetSupport done = new SupportClass.HashSetSupport(); System.Xml.XmlNodeList all = segmentElement.ChildNodes; for (int i = 0; i < all.Count; i++) { System.String elementName = all.Item(i).Name; if (System.Convert.ToInt16(all.Item(i).NodeType) == (short) System.Xml.XmlNodeType.Element && !done.Contains(elementName)) { done.Add(elementName); int index = elementName.IndexOf('.'); if (index >= 0 && elementName.Length > index) { //properly formatted element System.String fieldNumString = elementName.Substring(index + 1); int fieldNum = System.Int32.Parse(fieldNumString); parseReps(segmentObject, segmentElement, elementName, fieldNum); } else { } } } //set data type of OBX-5 if (segmentObject.GetType().FullName.IndexOf("OBX") >= 0) { Varies.fixOBX5(segmentObject, Factory); } }
public static System.String encode(Segment source, NuGenEncodingCharacters encodingChars) { System.Text.StringBuilder result = new System.Text.StringBuilder(); result.Append(source.getName()); result.Append(encodingChars.FieldSeparator); //start at field 2 for MSH segment because field 1 is the field delimiter int startAt = 1; if (isDelimDefSegment(source.getName())) startAt = 2; //loop through fields; for every field delimit any repetitions and add field delimiter after ... int numFields = source.numFields(); for (int i = startAt; i <= numFields; i++) { try { Type[] reps = source.getField(i); for (int j = 0; j < reps.Length; j++) { System.String fieldText = encode(reps[j], encodingChars); //if this is MSH-2, then it shouldn't be escaped, so unescape it again if (isDelimDefSegment(source.getName()) && i == 2) fieldText = NuGenEscape.unescape(fieldText, encodingChars); result.Append(fieldText); if (j < reps.Length - 1) result.Append(encodingChars.RepetitionSeparator); } } catch (NuGenHL7Exception) { } result.Append(encodingChars.FieldSeparator); } //strip trailing delimiters ... return stripExtraDelimiters(result.ToString(), encodingChars.FieldSeparator); }
/// <summary>Returns the expected XML element name for the given child of the given Segment </summary> private System.String makeElementName(Segment s, int child) { return s.getName() + "." + child; }
/// <summary>Returns the expected XML element name for the given child of the given Segment </summary> private System.String makeElementName(Segment s, int child) { return(s.getName() + "." + child); }
/// <summary> Parses a segment string and populates the given Segment object. Unexpected fields are /// added as Varies' at the end of the segment. /// /// </summary> /// <throws> HL7Exception if the given string does not contain the </throws> /// <summary> given segment or if the string is not encoded properly /// </summary> public virtual void parse(Segment destination, System.String segment, NuGenEncodingCharacters encodingChars) { int fieldOffset = 0; if (isDelimDefSegment(destination.getName())) { fieldOffset = 1; //set field 1 to fourth character of string Terser.set_Renamed(destination, 1, 0, 1, 1, System.Convert.ToString(encodingChars.FieldSeparator)); } System.String[] fields = split(segment, System.Convert.ToString(encodingChars.FieldSeparator)); //destination.setName(fields[0]); for (int i = 1; i < fields.Length; i++) { System.String[] reps = split(fields[i], System.Convert.ToString(encodingChars.RepetitionSeparator)); //MSH-2 will get split incorrectly so we have to fudge it ... bool isMSH2 = isDelimDefSegment(destination.getName()) && i + fieldOffset == 2; if (isMSH2) { reps = new System.String[1]; reps[0] = fields[i]; } for (int j = 0; j < reps.Length; j++) { try { System.Text.StringBuilder statusMessage = new System.Text.StringBuilder("Parsing field "); statusMessage.Append(i + fieldOffset); statusMessage.Append(" repetition "); statusMessage.Append(j); //parse(destination.getField(i + fieldOffset, j), reps[j], encodingChars, false); Type field = destination.getField(i + fieldOffset, j); if (isMSH2) { Terser.getPrimitive(field, 1, 1).Value = reps[j]; } else { parse(field, reps[j], encodingChars); } } catch (NuGenHL7Exception e) { //set the field location and throw again ... e.FieldPosition = i; e.SegmentRepetition = MessageIterator.getIndex(destination.Parent, destination).rep; e.SegmentName = destination.getName(); throw e; } } } //set data type of OBX-5 if (destination.GetType().FullName.IndexOf("OBX") >= 0) { Varies.fixOBX5(destination, Factory); } }
/// <summary> Logs the given exception and creates an error message to send to the /// remote system. /// /// </summary> /// <param name="encoding">The encoding for the error message. If <code>null</code>, uses default encoding /// </param> public static System.String logAndMakeErrorMessage(System.Exception e, Segment inHeader, Parser p, System.String encoding) { // create error message ... System.String errorMessage = null; try { Message out_Renamed = NuGenDefaultApplication.makeACK(inHeader); Terser t = new Terser(out_Renamed); //copy required data from incoming message ... try { t.set_Renamed("/MSH-10", MessageIDGenerator.Instance.NewID); } catch (System.IO.IOException ioe) { throw new NuGenHL7Exception("Problem creating error message ID: " + ioe.Message); } //populate MSA ... t.set_Renamed("/MSA-1", "AE"); //should this come from HL7Exception constructor? t.set_Renamed("/MSA-2", Terser.get_Renamed(inHeader, 10, 0, 1, 1)); System.String excepMessage = e.Message; if (excepMessage != null) t.set_Renamed("/MSA-3", excepMessage.Substring(0, (System.Math.Min(80, excepMessage.Length)) - (0))); /* Some earlier ACKs don't have ERRs, but I think we'll change this within HAPI so that there is a single ACK for each version (with an ERR). */ //see if it's an HL7Exception (so we can get specific information) ... if (e.GetType().Equals(typeof(NuGenHL7Exception))) { Segment err = (Segment) out_Renamed.get_Renamed("ERR"); ((NuGenHL7Exception) e).populate(err); } else { t.set_Renamed("/ERR-1-4-1", "207"); t.set_Renamed("/ERR-1-4-2", "Application Internal Error"); t.set_Renamed("/ERR-1-4-3", "HL70357"); } if (encoding != null) { errorMessage = p.encode(out_Renamed, encoding); } else { errorMessage = p.encode(out_Renamed); } } catch (System.IO.IOException ioe) { throw new NuGenHL7Exception("IOException creating error response message: " + ioe.Message, NuGenHL7Exception.APPLICATION_INTERNAL_ERROR); } return errorMessage; }
public static void Main(System.String[] args) { if (args.Length != 1) { System.Console.Out.WriteLine("Usage: XMLParser pipe_encoded_file"); System.Environment.Exit(1); } //read and parse message from file try { NuGenPipeParser parser = new NuGenPipeParser(); System.IO.FileInfo messageFile = new System.IO.FileInfo(args[0]); long fileLength = SupportClass.FileLength(messageFile); System.IO.StreamReader r = new System.IO.StreamReader(messageFile.FullName, System.Text.Encoding.Default); char[] cbuf = new char[(int)fileLength]; System.Console.Out.WriteLine("Reading message file ... " + r.Read((System.Char[])cbuf, 0, cbuf.Length) + " of " + fileLength + " chars"); r.Close(); System.String messString = System.Convert.ToString(cbuf); Message mess = parser.parse(messString); System.Console.Out.WriteLine("Got message of type " + mess.GetType().FullName); Genetibase.NuGenHL7.parser.NuGenXMLParser xp = new AnonymousClassXMLParser(); //loop through segment children of message, encode, print to console System.String[] structNames = mess.Names; for (int i = 0; i < structNames.Length; i++) { Structure[] reps = mess.getAll(structNames[i]); for (int j = 0; j < reps.Length; j++) { if (typeof(Segment).IsAssignableFrom(reps[j].GetType())) { //ignore groups System.Xml.XmlDocument docBuilder = new System.Xml.XmlDocument(); System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); //new doc for each segment System.Xml.XmlElement root = doc.CreateElement(reps[j].GetType().FullName); doc.AppendChild(root); xp.encode((Segment)reps[j], root); System.IO.StringWriter out_Renamed = new System.IO.StringWriter(); System.Console.Out.WriteLine("Segment " + reps[j].GetType().FullName + ": \r\n" + out_Renamed.ToString()); System.Type[] segmentConstructTypes = new System.Type[] { typeof(Message) }; System.Object[] segmentConstructArgs = new System.Object[] { null }; Segment s = (Segment)reps[j].GetType().GetConstructor(segmentConstructTypes).Invoke(segmentConstructArgs); xp.parse(s, root); System.Xml.XmlDocument doc2 = new System.Xml.XmlDocument(); System.Xml.XmlElement root2 = doc2.CreateElement(s.GetType().FullName); doc2.AppendChild(root2); xp.encode(s, root2); System.IO.StringWriter out2 = new System.IO.StringWriter(); if (out2.ToString().Equals(out_Renamed.ToString())) { System.Console.Out.WriteLine("Re-encode OK"); } else { System.Console.Out.WriteLine("Warning: XML different after parse and re-encode: \r\n" + out2.ToString()); } } } } } catch (System.Exception e) { SupportClass.WriteStackTrace(e, Console.Error); } }
/// <summary> Populates certain required fields in a response message header, using /// information from the corresponding inbound message. The current time is /// used for the message time field, and <code>MessageIDGenerator</code> is /// used to create a unique message ID. Version and message type fields are /// not populated. /// </summary> public static void fillResponseHeader(Segment inbound, Segment outbound) { if (!inbound.getName().Equals("MSH") || !outbound.getName().Equals("MSH")) throw new NuGenHL7Exception("Need MSH segments. Got " + inbound.getName() + " and " + outbound.getName()); //get MSH data from incoming message ... System.String encChars = Terser.get_Renamed(inbound, 2, 0, 1, 1); System.String fieldSep = Terser.get_Renamed(inbound, 1, 0, 1, 1); System.String procID = Terser.get_Renamed(inbound, 11, 0, 1, 1); //populate outbound MSH using data from inbound message ... Terser.set_Renamed(outbound, 2, 0, 1, 1, encChars); Terser.set_Renamed(outbound, 1, 0, 1, 1, fieldSep); System.Globalization.GregorianCalendar now = new System.Globalization.GregorianCalendar(); SupportClass.CalendarManager.manager.SetDateTime(now, System.DateTime.Now); Terser.set_Renamed(outbound, 7, 0, 1, 1, CommonTS.toHl7TSFormat(now)); Terser.set_Renamed(outbound, 10, 0, 1, 1, MessageIDGenerator.Instance.NewID); Terser.set_Renamed(outbound, 11, 0, 1, 1, procID); }
/// <summary> Creates an ACK message with the minimum required information from an inbound message. /// Optional fields can be filled in afterwards, before the message is returned. Pleaase /// note that MSH-10, the outbound message control ID, is also set using the class /// <code>Genetibase.NuGenHL7.util.MessageIDGenerator</code>. Also note that the ACK messages returned /// is the same version as the version stated in the inbound MSH if there is a generic ACK for that /// version, otherwise a version 2.4 ACK is returned. MSA-1 is set to AA by default. /// /// </summary> /// <param name="inboundHeader">the MSH segment if the inbound message /// </param> /// <throws> IOException if there is a problem reading or writing the message ID file </throws> /// <throws> DataTypeException if there is a problem setting ACK values </throws> public static Message makeACK(Segment inboundHeader) { if (!inboundHeader.getName().Equals("MSH")) throw new NuGenHL7Exception("Need an MSH segment to create a response ACK (got " + inboundHeader.getName() + ")"); //make ACK of correct version System.String version = null; try { version = Terser.get_Renamed(inboundHeader, 12, 0, 1, 1); } catch (NuGenHL7Exception) { /* proceed with null */ } if (version == null) version = "2.4"; System.String ackClassName = SourceGenerator.getVersionPackageName(version) + "message.ACK"; Message out_Renamed = null; try { System.Type ackClass = System.Type.GetType(ackClassName); out_Renamed = (Message) System.Activator.CreateInstance(ackClass); } catch (System.Exception e) { throw new NuGenHL7Exception("Can't instantiate ACK of class " + ackClassName + ": " + e.GetType().FullName); } Terser terser = new Terser(out_Renamed); //populate outbound MSH using data from inbound message ... Segment outHeader = (Segment) out_Renamed.get_Renamed("MSH"); fillResponseHeader(inboundHeader, outHeader); terser.set_Renamed("/MSH-9", "ACK"); terser.set_Renamed("/MSH-12", version); terser.set_Renamed("/MSA-1", "AA"); terser.set_Renamed("/MSA-2", Genetibase.NuGenHL7.util.NuGenTerser.get_Renamed(inboundHeader, 10, 0, 1, 1)); return out_Renamed; }
private void parseReps(Segment segmentObject, System.Xml.XmlElement segmentElement, System.String fieldName, int fieldNum) { System.Xml.XmlNodeList reps = segmentElement.GetElementsByTagName(fieldName); for (int i = 0; i < reps.Count; i++) { parse(segmentObject.getField(fieldNum, i), (System.Xml.XmlElement) reps.Item(i)); } }
/// <summary> Populates the given error segment with information from this Exception.</summary> public virtual void populate(Segment errorSegment) { //make sure it's an ERR if (!errorSegment.getName().Equals("ERR")) throw new NuGenHL7Exception("Can only populate an ERR segment with an exception -- got: " + errorSegment.GetType().FullName); int rep = errorSegment.getField(1).Length; //append after existing reps if (this.SegmentName != null) Terser.set_Renamed(errorSegment, 1, rep, 1, 1, this.SegmentName); if (this.SegmentRepetition >= 0) Terser.set_Renamed(errorSegment, 1, rep, 2, 1, System.Convert.ToString(this.SegmentRepetition)); if (this.FieldPosition >= 0) Terser.set_Renamed(errorSegment, 1, rep, 3, 1, System.Convert.ToString(this.FieldPosition)); Terser.set_Renamed(errorSegment, 1, rep, 4, 1, System.Convert.ToString(this.errCode)); Terser.set_Renamed(errorSegment, 1, rep, 4, 3, "hl70357"); Terser.set_Renamed(errorSegment, 1, rep, 4, 5, this.Message); //try to get error condition text try { System.String desc = NuGenTableRepository.Instance.getDescription(357, System.Convert.ToString(this.errCode)); Terser.set_Renamed(errorSegment, 1, rep, 4, 2, desc); } catch (NuGenLookupException) { } }
private void parse(System.String field, Segment segment, int num, NuGenEncodingCharacters ec) { if (field != null) { int rep = 0; int component = 1; int subcomponent = 1; Type type = segment.getField(num, rep); System.String delim = System.Convert.ToString(new char[]{ec.RepetitionSeparator, ec.ComponentSeparator, ec.SubcomponentSeparator}); for (SupportClass.Tokenizer tok = new SupportClass.Tokenizer(field, delim, true); tok.HasMoreTokens(); ) { System.String token = tok.NextToken(); char c = token[0]; if (c == ec.RepetitionSeparator) { rep++; component = 1; subcomponent = 1; type = segment.getField(num, rep); } else if (c == ec.ComponentSeparator) { component++; subcomponent = 1; } else if (c == ec.SubcomponentSeparator) { subcomponent++; } else { Primitive p = Terser.getPrimitive(type, component, subcomponent); p.Value = token; } } } }
/// <summary> Ensures that there is an empty repetition of the underlying message Structure. </summary> /// <returns>s true if successful, false if structure is non-repeating and full. /// </returns> private bool prepEmptyInstance() { bool success = false; if (this.currSegment == null || (this.repeating && this.currSegmentFull)) { int numExisting = parent.getAll(this.name).Length; this.currSegment = (Segment) parent.get_Renamed(this.name, numExisting); //get next rep this.currSegmentFull = false; success = true; } else if (!this.currSegmentFull) { success = true; } return success; }