/// <param name="message">a parsed message to validate (note that MSH-9-1 and MSH-9-2 must be valued) /// </param> /// <returns> true if the message is OK /// </returns> /// <throws> HL7Exception if there is at least one error and this validator is set to fail on errors </throws> public virtual bool validate(Message message) { Terser t = new Terser(message); MessageRule[] rules = myContext.getMessageRules(message.Version, t.Get("MSH-9-1"), t.Get("MSH-9-2")); ValidationException toThrow = null; bool result = true; for (int i = 0; i < rules.Length; i++) { ValidationException[] ex = rules[i].test(message); for (int j = 0; j < ex.Length; j++) { result = false; ourLog.error("Invalid message", ex[j]); if (failOnError && toThrow == null) { toThrow = ex[j]; } } } if (toThrow != null) { throw new HL7Exception("Invalid message", toThrow); } return(result); }
/// <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(msh, 1, 0, 1, 1); if (fieldSepString == null) { throw new HL7Exception("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(msh, 2, 0, 1, 1); if (encCharString == null) { throw new HL7Exception("Can't encode message: MSH-2 (encoding characters) is missing"); } if (encCharString.Length != 4) { throw new HL7Exception("Encoding characters '" + encCharString + "' invalid -- must be 4 characters", HL7Exception.DATA_TYPE_ERROR); } EncodingCharacters en = new EncodingCharacters(fieldSep, encCharString); //pass down to group encoding method which will operate recursively on children ... return(encode((Group)source, en)); }
/// <summary> Formats a Message object into an html table object using the given /// </summary> /// <throws> HL7Exception if the data fields in the message do not permit encoding </throws> /// <summary> (e.g. required fields are null) /// </summary> /// <throws> EncodingNotSupportedException if the requested encoding is not </throws> /// <summary> supported by this parser. /// </summary> public Table buildTable(Message source) { //get encoding characters ... Segment msh = (Segment)source.get_Renamed("MSH"); System.String fieldSepString = Terser.Get(msh, 1, 0, 1, 1); if (fieldSepString == null) { throw new HL7Exception("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(msh, 2, 0, 1, 1); EncodingCharacters en = new EncodingCharacters(fieldSep, encCharString); //pass down to group encoding method which will operate recursively on children ... Table tbl = new Table(); encode((Group)source, en, tbl); return(tbl); }
/// <param name="message">a parsed message to validate (note that MSH-9-1 and MSH-9-2 must be valued) /// </param> /// <returns> true if the message is OK /// </returns> /// <throws> HL7Exception if there is at least one error and this validator is set to fail on errors </throws> public virtual bool validate(Message message) { Terser t = new Terser(message); MessageRule[] rules = myContext.getMessageRules(message.Version, t.Get("MSH-9-1"), t.Get("MSH-9-2")); ValidationException toThrow = null; bool result = true; for (int i = 0; i < rules.Length; i++) { ValidationException[] ex = rules[i].test(message); for (int j = 0; j < ex.Length; j++) { result = false; ourLog.error("Invalid message", ex[j]); if (failOnError && toThrow == null) { toThrow = ex[j]; } } } if (toThrow != null) { throw new HL7Exception("Invalid message", toThrow); } return result; }
/// <summary> Fills a field with values from an unparsed string representing the field. </summary> /// <param name="destinationField">the field Type /// </param> /// <param name="data">the field string (including all components and subcomponents; not including field delimiters) /// </param> /// <param name="encodingCharacters">the encoding characters used in the message /// </param> private static void parse(Type destinationField, System.String data, EncodingCharacters encodingCharacters) { System.String[] components = split(data, System.Convert.ToString(encodingCharacters.ComponentSeparator)); for (int i = 0; i < components.Length; i++) { System.String[] subcomponents = split(components[i], System.Convert.ToString(encodingCharacters.SubcomponentSeparator)); for (int j = 0; j < subcomponents.Length; j++) { System.String val = subcomponents[j]; if (val != null) { val = Escape.unescape(val, encodingCharacters); } Terser.getPrimitive(destinationField, i + 1, j + 1).Value = val; } } }
/// <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 = Parser.makeControlMSH(version, Factory); Terser.Set(criticalData, 1, 0, 1, 1, parseLeaf(message, "MSH.1", 0)); Terser.Set(criticalData, 2, 0, 1, 1, parseLeaf(message, "MSH.2", 0)); Terser.Set(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(criticalData, 11, 0, 1, 1, procID); return(criticalData); }
/// <summary> Encodes the given Type, using the given encoding characters. /// It is assumed that the Type represents a complete field rather than a component. /// </summary> public static System.String encode(Type source, EncodingCharacters encodingChars) { System.Text.StringBuilder field = new System.Text.StringBuilder(); for (int i = 1; i <= Terser.numComponents(source); i++) { System.Text.StringBuilder comp = new System.Text.StringBuilder(); for (int j = 1; j <= Terser.numSubComponents(source, i); j++) { Primitive p = Terser.getPrimitive(source, i, j); comp.Append(encodePrimitive(p, encodingChars)); comp.Append(encodingChars.SubcomponentSeparator); } field.Append(stripExtraDelimiters(comp.ToString(), encodingChars.SubcomponentSeparator)); field.Append(encodingChars.ComponentSeparator); } return(stripExtraDelimiters(field.ToString(), encodingChars.ComponentSeparator)); //return encode(source, encodingChars, false); }
/// <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.getStructureName().Equals("ERR")) { throw new HL7Exception("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(errorSegment, 1, rep, 1, 1, this.SegmentName); } if (this.SegmentRepetition >= 0) { Terser.Set(errorSegment, 1, rep, 2, 1, System.Convert.ToString(this.SegmentRepetition)); } if (this.FieldPosition >= 0) { Terser.Set(errorSegment, 1, rep, 3, 1, System.Convert.ToString(this.FieldPosition)); } Terser.Set(errorSegment, 1, rep, 4, 1, System.Convert.ToString(this.errCode)); Terser.Set(errorSegment, 1, rep, 4, 3, "hl70357"); //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.getMessage' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" Terser.Set(errorSegment, 1, rep, 4, 5, this.Message); //try to get error condition text try { System.String desc = TableRepository.Instance.getDescription(357, System.Convert.ToString(this.errCode)); Terser.Set(errorSegment, 1, rep, 4, 2, desc); } catch (LookupException e) { ourLog.debug("Warning: LookupException getting error condition text (are we connected to a TableRepository?)", e); } }
/// <summary> Encodes the given Type, using the given encoding characters. /// It is assumed that the Type represents a complete field rather than a component. /// </summary> protected virtual Control encode(Type source, EncodingCharacters encodingChars) { if (source is ca.uhn.hl7v2.model.Composite) { TableCell td; TableRow tr; Table tbl = new Table(); tbl.Attributes["border"] = _componentBorder.ToString(); tbl.CellSpacing = _componentCellSpacing; tbl.CellPadding = _componentCellPadding; for (int i = 1; i <= Terser.numComponents(source); i++) { for (int j = 1; j <= Terser.numSubComponents(source, i); j++) { tr = new TableRow(); Primitive p = Terser.getPrimitive(source, i, j); Type type = (Type)p; td = new TableCell(); if (_cssComponentField != null) { td.CssClass = _cssComponentField; } string desc = " "; if (type.Description != null && type.Description.Trim().Length > 0) { desc = type.Description; } if (j > 1) { desc = " -" + desc; } td.Text = desc; tr.Cells.Add(td); string val = " "; if (p.Value != null && p.Value.Trim().Length > 0) { val = p.Value; val = val.Replace(@"\.br\", "<BR>"); } td = new TableCell(); td.Text = val; tr.Cells.Add(td); tbl.Rows.Add(tr); } } return(tbl); } else { Primitive p = Terser.getPrimitive(source, 1, 1); string val = " "; if (p.Value != null && p.Value.Trim().Length > 0) { val = p.Value; val = val.Replace(@"\.br\", "<BR>"); } return(new LiteralControl(val)); } }
/// <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 HL7Exception("Couldn't find MSH segment in message: " + message, HL7Exception.SEGMENT_SEQUENCE_ERROR); } //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" 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 = Parser.makeControlMSH(version, Factory); Terser.Set(msh, 1, 0, 1, 1, System.Convert.ToString(fieldSep)); Terser.Set(msh, 2, 0, 1, 1, encChars); Terser.Set(msh, 10, 0, 1, 1, messControlID); Terser.Set(msh, 11, 0, 1, 1, procIDComps[0]); } catch (System.Exception e) { SupportClass.WriteStackTrace(e, Console.Error); //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Class.getName' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.getMessage' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" throw new HL7Exception("Can't parse critical fields from MSH segment (" + e.GetType().FullName + ": " + e.Message + "): " + mshString, HL7Exception.REQUIRED_FIELD_MISSING); } return(msh); }
/// <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, EncodingCharacters encodingChars) { int fieldOffset = 0; if (isDelimDefSegment(destination.getStructureName())) { fieldOffset = 1; //set field 1 to fourth character of string Terser.Set(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)); if (log.DebugEnabled) { log.debug(reps.Length + "reps delimited by: " + encodingChars.RepetitionSeparator); } //MSH-2 will get split incorrectly so we have to fudge it ... bool isMSH2 = isDelimDefSegment(destination.getStructureName()) && 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); log.debug(statusMessage.ToString()); //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 (HL7Exception e) { //set the field location and throw again ... e.FieldPosition = i; e.SegmentRepetition = MessageIterator.getIndex(destination.ParentGroup, destination).rep; e.SegmentName = destination.getStructureName(); throw e; } } } //set data type of OBX-5 if (destination.GetType().FullName.IndexOf("OBX") >= 0) { Varies.fixOBX5(destination, Factory); } }