001    package org.vmdb.hl7;
002    
003    import java.util.*;
004    
005    /**
006     * <p><Title:> Observation Unsolicited (ORU) Message. </p>
007     * <p>Description: HL7 Network Connectivity For VMDB. </p>
008     * <p>Copyright: Copyright (c) 2002-2003. </p>
009     * <p>Company: Veterinary Medical Database (VMDB). </p>
010     * <p>Message class(es) form the main API for the library.  In most cases, there
011     * will be simple methods for inserting and extracting values from the message
012     * structure as well as for parsing incoming messages or building outgoing
013     * messages in either delimited HL7 or the draft XML representation.</p>
014     * <p>The Observation Unsolicited (ORU) message is the approved method for
015     * submitting data to the Veterinary Medical Database (VMDB).  This class
016     * implements a subset of the ORU message that meets all requirements of VMDB
017     * and parses and builds standard-compliant ORU messages.  Appropriate default
018     * values are provided for required fields not used by VMDB.  Limitations
019     * added by VMDB are supported and taken advantage of in order to keep the API
020     * as simple as possible.  For routine processing of VMDB submissions, the only
021     * methods needed should be those provided in the ORUMessage's public interface and
022     * the public methods inherited from HL7Message.</p>
023     * <p>For more complex tasks or for anything I've missed, <i>any</i>
024     * standard-compliant ORU message should parse
025     * correctly and any of its contents be accessible using lower-level classes.
026     * Similarly, using lower-level classes it is possible to build any standard-compliant
027     * ORU message which can then be rendered as either delimited or XML version HL7.</p>
028     * <p>The XML representation of HL7 2.x introduces the concepts of groups and lists
029     * that, while present in the construction rules of delimited HL7, are not explicitly
030     * named or labelled in the messages themselves.  The internal representation in
031     * this class follows the XML version with explicitly constructed groups and lists
032     * which we've combined under the general term (borrowed from X12) of "loop."  Going
033     * to the lower-level access methods often requires knowing the location of a segment
034     * in this loop structure.  Perhaps the simplest way to learn this loop structure
035     * is to use the library to import a delimited HL7 message of the structure you are
036     * working with, and then print it out using the toXML() method.  Because all the
037     * groups and lists are explicitly named in the XML, the structure hierarchy will
038     * be right there.</p>
039     * <p>The internal representation is built up using recursive construction.  The
040     * message contains segments and loops.  Loops can contain segments and other loops.
041     * Segments contain fields defined by elements of specific HL7 types.  These elements my
042     * be simple strings of various types (all represented by SimpleElement currently) or may
043     * be built up from components which are themselves elements and so on.  Because any
044     * given message can contain a potentially infinite number of segments, the data
045     * structure is only instantiated as it is needed either from reading in segments
046     * during parsing or from setting values that reside in a segment or field not yet
047     * instantiated.  This is actually implemented in the getSSS() methods on the basis
048     * that a program is only getting the segment to do something with it.  Similarly,
049     * segments are intially instantiated with place holders for all fields up to the
050     * last required field.  This ensures that all delimiters required for standards
051     * compliance are present when the structure is output as HL7.  If a value is set
052     * for an optional field after the last required field, placeholders up to and including
053     * the new field are instantiated.  Because XML uses tag labels rather than position
054     * to identify fields, these empty fields are not output in the toXML() method.</p>
055     * <p>Note: We use Java Bean like convention of getXXX and setXXX but with a few
056     * deviations.  First as previously mentioned, getSSS() where SSS is a segment or loop
057     * abbreviation has the side affect of constructing the segment or loop if it does not already
058     * exist.  Also, the set methods frequently take multiple parameters especially when the
059     * actual value is a mildly complex element-type such as coded entry (CE).  We have
060     * added common conventional name patterns.  For example addSSS() for segments that
061     * repeat and addDiagnosis(), etc., to abstract
062     * the process of adding a segment (usually OBX) holding a single data entry such
063     * as a finding (addFinding) or diagnosis (addDiagnosis).  listXXX() returns an Iterator
064     * object over a collection of segments of the type specified by XXX.  findEEE( XXX )
065     * methods perform a depth-first search of the object hierarchy looking for XXX.</p>
066     * <p>TODO: Add string length validation to all methods taking String input and
067     * add to documentation.</p>
068     * @author Michael K. Martin
069     * @version 1.0
070     */
071    
072    public class ORUMessage extends HL7Message {
073       private String sNm = "ORU_R01";
074       private String sRl = "MSH PATIENT_RESULT[DSC]";
075       private static final String MESSAGE_TYPE = "ORU";
076       private static final String MESSAGE_EVENT = "R01";
077    
078       /**
079        * Construct an empty ORU Message with type and version set.  This object
080        * is ready to either build via a series of set and add calls, or to read in
081        * an HL7 or XML (future) message.
082        */
083       public ORUMessage() {
084          super();
085          setName( sNm );
086          setRule( sRl );
087            // This is the first and only event for this message so we can
088            // use the default message structure.
089          setMessageName( MESSAGE_TYPE, MESSAGE_EVENT );
090       }
091    
092       /**
093        * Get the The PATIENT_RESULT loop contained in this message.  If
094        * it does not exist, take the necessary steps to create it.<br><br>
095        * Chaining calls to get[*]Loop() allows the retrieval of
096        * a loop at any point in the hierarchy with knowledge that if this is the
097        * first use of any loop or loops in the hierarchy, they will be created
098        * so that the returned reference is a valid part of the message being
099        * constructed.  The referenced loop can then be used to access a segment
100        * via getSSS() or addSSS() depending on the type repeatablility of the segment.
101        * @return A reference to the PATIENT_RESULTLoop object located at the
102        * appropriate place in the loop.
103        */
104       public PATIENT_RESULTLoop getPATIENT_RESULT() {
105          PATIENT_RESULTLoop loop = null;
106          if( vSegments == null ) vSegments = new Vector();
107          for( Iterator i = vSegments.iterator(); i.hasNext(); ) {
108             HL7Object o = (HL7Object)i.next();
109             if( o.getName().equals( "PATIENT_RESULT" ) ) {
110                loop = (PATIENT_RESULTLoop)o;
111                break;
112             }
113          }
114          if( loop == null ) {
115             loop = new PATIENT_RESULTLoop();
116             // make this the second segment if we have an MSH already
117             // otherwise put it at the beginning before DSC
118             int iLoc = (findSegment("MSH") != null) ? 1 : 0;
119             vSegments.add(iLoc, loop);
120          }
121          return loop;
122       }
123    
124       /**
125        * Get the PID segment that should be nested within this message.  If
126        * it does not exist, take the necessary steps to create it in the appropriate
127        * nested loop structure.<br><br>
128        * Note: The "get" semantics here only work correctly for segments that exist singly
129        * in a message.  We break with generic HL7 here in that only under VMDB are
130        * we restricted to a single patient per message.  If we drop this limitation
131        * we will need to create more complex "addPID()" semantics.
132        * @return A reference to the PIDSegment object located at the appropriate place.
133        */
134       public PIDSegment getPID() {
135          PATIENTLoop loop = getPATIENT_RESULT().getPATIENT();
136          PIDSegment pid = loop.getPID();
137          return pid;
138       }
139    
140       /**
141        * Get the PV1 segment that should be nested somewhere within this loop.  If
142        * it does not exist, take the necessary steps to create it in the appropriate
143        * nested loop structure.<br><br>
144        * Note: The "get" semantics here only work correctly for segments that exist singly
145        * in a message.  We break with generic HL7 here in that only under VMDB are
146        * we restricted to a single patient visit per message.  If we drop this limitation
147        * we will need to create more complex "addPV1()" semantics.
148        * @return A reference to the PV1Segment object located at the appropriate place in the loop.
149        */
150       public PV1Segment getPV1() {
151          PATIENT_VISITLoop loop = getPATIENT_RESULT().getPATIENT().getPATIENT_VISIT();
152          PV1Segment pv1 = loop.getPV1();
153          return pv1;
154       }
155    
156       /**
157        * Get the OBR segment that should be nested somewhere within this loop.  If
158        * it does not exist, take the necessary steps to create it in the appropriate
159        * nested loop structure.<br><br>
160        * Note: The "get" semantics here only work correctly for segments that exist singly
161        * in a message.  We break with generic HL7 here in that only under VMDB are
162        * we restricted to a single observation request per message.  If we drop this limitation
163        * we will need to create more complex "addOBR()" semantics.
164        * @return a reference to the OBRSegment object located at the appropriate place in the loop.
165        */
166       public OBRSegment getOBR() {
167          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
168          OBRSegment obr = loop.getOBR();
169          return obr;
170       }
171    
172       /**
173        * Get the ORC segment that should be nested somewhere within this loop.  If
174        * it does not exist, take the necessary steps to create it in the appropriate
175        * nested loop structure.<br><br>
176        * @return a reference to the OBRSegment object located at the appropriate place in the loop.
177        */
178       public ORCSegment getORC() {
179          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
180          ORCSegment orc = loop.getORC();
181          return orc;
182       }
183    
184       /**
185        * Get the ORC segment that should be nested somewhere within this loop.  If
186        * it does not exist, take the necessary steps to create it in the appropriate
187        * nested loop structure.<br><br>
188        * @return a reference to the OBRSegment object located at the appropriate place in the loop.
189        */
190       public ORCSegment getORC( String sOrderControl ) {
191          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION(sOrderControl);
192          ORCSegment orc = loop.getORC();
193          return orc;
194       }
195    
196       /**
197        * Add an OBX segment that should be located in this loop.  If the loop
198        * does not exist, take the necessary steps to create it.<br><br>
199        * Note:  This should really be linked to a specific OBR, but because
200        * VMDB only allows one OBR per message, we are safe like this.
201        * @return OBXSegment object newly created.
202        */
203       public OBXSegment addOBX() {
204          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
205          OBXSegment obx = loop.addOBX();
206          return obx;
207       }
208    
209    
210       // The accessors and mutators are based on VMDB usage of the ORU Message
211       // and are designed to provide easy access to message details
212    
213       // OK, this is dumb, but the first four are just to get JavaDoc to include
214       // these directly in the ORUMessage documentation.
215       /**
216        * Set the message control id.
217        * @param sMessageId String with unique identifier
218        */
219       public void setMessageControlId( String sMessageControlId ) {
220          super.setMessageControlId( sMessageControlId );
221       }
222    
223       /**
224        * Get the message control id.
225        * @return String with unique identifier
226        */
227       public String getMessageControlId() {
228          return super.getMessageControlId();
229       }
230    
231       /**
232        * Set the Sending facility VMDB Institution Id.
233        * @param sName String with VMDB facility name
234        * @param sId String with VMDB facility id
235        */
236       public void setSendingFacility( String sName, String sId ) {
237          super.setSendingFacility( sName, sId );
238       }
239    
240       /**
241        * Get the Sending facility VMDB Institution Id.
242        * @return String with VMDB facility id
243        */
244       public String getSendingFacilityId() {
245          return super.getSendingFacilityId();
246       }
247    
248       /**
249        * Get the Sending facility name.
250        * @return String with VMDB facility name
251        */
252       public String getSendingFacilityName() {
253          MSHSegment msh = getMSH();
254          return msh.getSendingFacilityName();
255       }
256    
257       /**
258        * Set the Medical record number.<br><br>
259        * Medical record is defined in this logic as the identifier assigned by the
260        * sending facility.  To work correctly, the sending facility identifier must
261        * be set <strong>before</strong> actions involving MRN.
262        * @param sMRN String with facility's MRN
263        */
264       public void setMedicalRecordNumber( String sMRN ) {
265          MSHSegment msh = getMSH();
266          String sThisFacility = msh.getSendingFacilityName();
267          PIDSegment pid = getPID();
268          boolean bFound = false;
269          // First try to update existing MRN
270          Iterator i = pid.listPatientIds();
271          while( i != null && i.hasNext() ) {
272             CXElement cxe = (CXElement)i.next();
273             if( cxe == null ) continue;
274             // Check assigner logic
275             String sAssigner = cxe.getAssigningAuthorityName();
276             if( sAssigner != null && sAssigner.equals( sThisFacility ) ) {
277                cxe.setIdentifier( sMRN );
278                bFound = true;
279                break;
280             }
281          }
282          // Otherwise add a new identifier
283          if( !bFound ) {
284             pid.addPatientId( sMRN, sThisFacility );
285          }
286       }
287    
288       /**
289        * Get the Medical record number.<br><br>
290        * Medical record is defined in this logic as the identifier assigned by the
291        * sending facility.  To work correctly, the sending facility identifier must
292        * be set <strong><i>before</i></strong> actions involving MRN.
293        * @return String with facility's MRN
294        */
295       public String getMedicalRecordNumber() {
296          MSHSegment msh = getMSH();
297          String sThisFacility = msh.getSendingFacilityName();
298          PIDSegment pid = getPID();
299          // First try to update existing MRN
300          Iterator i = pid.listPatientIds();
301          while( i.hasNext() ) {
302             CXElement cxe = (CXElement)i.next();
303             // Check assigner logic
304             if( cxe.getAssigningAuthorityName().equals( sThisFacility ) ) {
305                return cxe.getIdentifier();
306             }
307          }
308          return "";
309       }
310    
311       /**
312        * Set the species as a predefined CE Element.<br><br>
313        * Often uses one of the set of predefined constants.<br>
314        * Note: If populated with a constant, subsequent calls to getSpecies() will return a
315        * reference to the constant (final static object) and cannot be modified
316        * through calls to the CEElement's methods.  But why would you want to?
317        * @param ceSpecies CEElement populated with the coded species
318        */
319       public void setSpecies( CEElement ceSpecies ) {
320          PIDSegment pid = getPID();
321          pid.setSpecies( ceSpecies );
322       }
323    
324       /**
325        * Set the species as the individual components of a CE Element
326        * @param sSpeciesCode String with the code for species
327        * @param sSpeciesText String with the species spelled out
328        * @param sCodeSystem String, usually "SCT," with the type of code
329        */
330       public void setSpecies( String sSpeciesCode,
331                               String sSpeciesText,
332                               String sCodeSystem ) {
333          PIDSegment pid = getPID();
334          pid.setSpecies( sSpeciesCode, sSpeciesText, sCodeSystem );
335       }
336    
337       /**
338        * Set the species as the individual components of a CE Element
339        * @param sSpeciesCode String with the code for species
340        * @param sSpeciesText String with the species spelled out
341        * @param sCodeSystem String, usually "SCT," with the type of code
342        * @param sAltSpeciesCode String with the alternate code for the SAME species
343        * @param sAltSpeciesText String with the species spelled out as in alternate system
344        * @param sAltCodeSystem String, alternate the type of code
345        */
346       public void setSpecies( String sSpeciesCode,
347                               String sSpeciesText,
348                               String sCodeSystem,
349                               String sAltSpeciesCode,
350                               String sAltSpeciesText,
351                               String sAltCodeSystem ) {
352          PIDSegment pid = getPID();
353          pid.setSpecies( sSpeciesCode, sSpeciesText, sCodeSystem,
354                          sAltSpeciesCode, sAltSpeciesText, sAltCodeSystem );
355       }
356    
357       /**
358        * Get the species as a predefined CE Element.<br><br>
359        * Often uses one of the set of predefined constants.<br>
360        * Note: If setSpecies was called with a constant, subsequent calls to getSpecies() will return a
361        * reference to the constant (final static object) and cannot be modified
362        * through calls to the CEElement's methods.  But why would you want to?
363        * @return A reference to the CEElement populated with species
364        */
365       public CEElement getSpecies() {
366          PIDSegment pid = getPID();
367          return pid.getSpecies();
368       }
369    
370       /**
371        * Get the species text.
372        * @return String species spelled out
373        */
374       public String getSpeciesText() {
375          PIDSegment pid = getPID();
376          return pid.getSpeciesText();
377       }
378    
379       /**
380        * Set the breed as a predefined CE Element.<br><br>
381        * Often uses one of the set of predefined constants.<br>
382        * Note: If populated with a constant, subsequent calls to getBreed() will return a
383        * reference to the constant (final static object) and cannot be modified
384        * through calls to the CEElement's methods.  But why would you want to?
385        * @param ceBreed CEElement populated with breed
386        */
387       public void setBreed( CEElement ceBreed ) {
388          PIDSegment pid = getPID();
389          pid.setBreed( ceBreed );
390       }
391    
392       /**
393        * Set the breed as the individual components of a CE Element.
394        * @param sBreedCode String snomed code for breed
395        * @param sBreedText String breed spelled out
396        * @param sCodeSystem String usually SCT with type of code
397        */
398       public void setBreed( String sBreedCode,
399                             String sBreedText,
400                             String sCodeSystem ) {
401          PIDSegment pid = getPID();
402          pid.setBreed( sBreedCode, sBreedText, sCodeSystem );
403       }
404    
405       /**
406        * Set the breed as the individual components of a CE Element.
407        * @param sBreedCode String snomed code for breed
408        * @param sBreedText String breed spelled out
409        * @param sCodeSystem String usually SCT with type of code
410        * @param sAltBreedCode String Alternate code for breed
411        * @param sAltBreedText String breed spelled out as in alternate system
412        * @param sAltCodeSystem String alternate type of code
413        */
414       public void setBreed( String sBreedCode,
415                             String sBreedText,
416                             String sCodeSystem,
417                             String sAltBreedCode,
418                             String sAltBreedText,
419                             String sAltCodeSystem ) {
420          PIDSegment pid = getPID();
421          pid.setBreed( sBreedCode, sBreedText, sCodeSystem ,
422                        sAltBreedCode, sAltBreedText, sAltCodeSystem);
423       }
424    
425       /**
426        * Get the breed as a predefined CE Element.<br><br>
427        * Often uses one of the set of predefined constants.<br>
428        * Note: If populated with a constant, subsequent calls to getBreed() will return a
429        * reference to the constant (final static object) and cannot be modified
430        * through calls to the CEElement's methods.  But why would you want to?
431        * @return A reference to the CEElement populated with breed
432        */
433       public CEElement getBreed() {
434          PIDSegment pid = getPID();
435          return pid.getBreed();
436       }
437    
438       /**
439        * Get the breed text.
440        * @return String breed spelled out
441        */
442       public String getBreedText() {
443          PIDSegment pid = getPID();
444          return pid.getBreedText();
445       }
446    
447       /**
448        * Set the date of birth as ISO format data string YYYYMMDD
449        * @param sDOB String with date of birth
450        */
451       public void setDateOfBirth( String sBDate ) {
452          PIDSegment pid = getPID();
453          pid.setDateOfBirth( sBDate );
454       }
455    
456       /**
457        * Set the date of birth as ISO format data string YYYYMMDD
458        * @param sDOB String with date of birth
459        */
460       public void setDateOfBirth( TSElement tsBDate ) {
461          PIDSegment pid = getPID();
462          pid.setDateOfBirth( tsBDate );
463       }
464    
465       /**
466        * Get the date of birth.
467        * @return String with date of birth as YYYYMMDD
468        */
469       public String getDateOfBirth() {
470          PIDSegment pid = getPID();
471          return pid.getDateOfBirth();
472       }
473    
474       /**
475        * Set the gender.
476        * @param sSex String with gender as M, C, T, F, S, O, U, H, or X
477        */
478       public void setSex( String sSex ) {
479          PIDSegment pid = getPID();
480          pid.setSex( sSex );
481       }
482    
483       /**
484        * Get the gender.
485        * @return String with gender code
486        */
487       public String getSex() {
488          PIDSegment pid = getPID();
489          return pid.getSex();
490       }
491    
492       /**
493        * Set the weight.<br><br>
494        * Stored as OBX with loinc code 8335-2 "Body Weight Estimated" or
495        * 3141-9 "Body Weight Measured"<br>
496        * TODO: Add checking that the string supplied as sWeight parses as a numeric.<br>
497        * TODO: Add checking that the string supplied as sWeightUnits is a valid unit of measure
498        * @param sWeight String with weight in numeric form<br>
499        * @param sWeightUnits String with units in ISO or ANSI abbreviation
500        * (usually kg or lb)<br>
501        * @param bMeasured true if weight was measured rather than estimated
502        * reflected internally by loinc code 3141-9 measured vs. 8335-2 est
503        * default is true
504        */
505       public void setWeight( String sWeight, String sWeightUnits, boolean bMeasured ) {
506          // Need to construct OBX with appropriate LOINC code for measured or estimated
507          // weight observation.
508          OBXSegment obx = addOBX();
509          if( bMeasured )
510             obx.setObservationIdentifier( Loinc.BODY_WEIGHT_MEASURED );
511          else
512             obx.setObservationIdentifier( Loinc.BODY_WEIGHT_ESTIMATED );
513          try {
514             obx.setObservationValueType( "NM" );
515             obx.setObservationValue( sWeight );
516             obx.setUnits( sWeightUnits );
517          }
518          catch( MalformedFieldException mfe ) {
519             mfe.printStackTrace();
520          }
521       }
522    
523       /**
524        * Set the animal's weight and weight units.<br><br>
525        * Stored as OBX with loinc code 3141-9 "Body Weight Measured."  This is
526        * just a convenience method for the common case where weights are normally
527        * always measured.
528        * @param sWeight String with weight in numeric form
529        * @param sWeightUnits String with units in ISO or ANSI abbreviation
530        * (usually kg or lb)
531        */
532       public void setWeight( String sWeight, String sWeightUnits ) {
533          setWeight( sWeight, sWeightUnits, true );
534       }
535    
536       /**
537        * Set the animal's weight range and weight units.<br><br>
538        * Stored as OBX with loinc code 3141-9 "Body Weight Measured."  This is
539        * just a convenience method for the common case where weights are normally
540        * always measured.
541        * @param snWeightRange SNElement with coded weight range
542        * @param sWeightUnits std::string with units in ISO or ANSI abbreviation
543        * (usually kg or lb)
544        */
545       public void setWeightRange( SNElement snWeightRange, CEElement ceWeightUnits ) {
546          // Need to construct OBX with appropriate LOINC code for measured or estimated
547          // weight observation.
548          OBXSegment obx = addOBX();
549          obx.setObservationIdentifier(  Loinc.BODY_WEIGHT_ESTIMATED.Clone() );
550          try {
551             obx.setObservationValueType( "SN" );
552             obx.setObservationValue( snWeightRange );
553             obx.setUnits( ceWeightUnits );
554          }
555          catch( MalformedFieldException mfe ) {
556             mfe.printStackTrace();
557          }
558       }
559    
560       /**
561        * Set the animal's weight range and weight units.<br><br>
562        * Stored as OBX with loinc code 3141-9 "Body Weight Measured."  This is
563        * just a convenience method for the common case where weights are normally
564        * always measured.
565        * @param sMin std::string with low end of weight range in numeric form
566        * @param sMax std::string with high end of weight range in numeric form
567        * @param sWeightUnits std::string with units in ISO or ANSI abbreviation
568        * (usually kg or lb)
569        */
570       public void setWeightRange( String sMin, String sMax, CEElement ceWeightUnits ) {
571          // Need to construct OBX with appropriate LOINC code for measured or estimated
572          // weight observation.
573          OBXSegment obx = addOBX();
574          obx.setObservationIdentifier(  Loinc.BODY_WEIGHT_ESTIMATED.Clone() );
575          try {
576             SNElement snWeightRange = new SNElement();
577             snWeightRange.setComparator( "=" );
578             snWeightRange.setNum1( sMin );
579             snWeightRange.setSeparator( "-" );
580             snWeightRange.setNum2( sMax );
581             obx.setObservationValueType( "SN" );
582             obx.setObservationValue( snWeightRange );
583             obx.setUnits( ceWeightUnits );
584          }
585          catch( MalformedFieldException mfe ) {
586             mfe.printStackTrace();
587          }
588       }
589    
590       /**
591        * Get the animal's weight.<br><br>
592        * Stored as OBX with loinc code 8335-2 "Body Weight Estimated" or
593        * 3141-9 "Body Weight Measured"<br>
594        * Note: We assume only one weight.  The logic here is to take the first found
595        * of measured or the first estimated if no measured found.  Equally valid would be
596        * to take the latest measurement, etc.
597        * @return String with weight
598        */
599       public String getWeight() {
600          String sWeight = "";
601          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
602          Iterator i = loop.findOBXSegments(Loinc.BODY_WEIGHT_MEASURED);
603          if( i.hasNext() ) {
604             OBXSegment obx = (OBXSegment)i.next();
605             sWeight = obx.getObservationValue().getValue();
606          }
607          else {
608             Iterator i2 = loop.findOBXSegments(Loinc.BODY_WEIGHT_ESTIMATED);
609             if( i2.hasNext() ) {
610                OBXSegment obx = (OBXSegment)i2.next();
611                HL7Element hlE = obx.getObservationValue();
612                if( hlE instanceof SNElement ) {
613                   SNElement snE = (SNElement)hlE;
614                   sWeight = snE.getValue();
615                }
616                else
617                   sWeight = hlE.getValue();
618             }
619    
620          }
621          return sWeight;
622       }
623    
624       /**
625        * Get the weight units.<br><br>
626        * Note:  Thelogic used here must be kept in step with the logic used
627        * in getWeight().  For now that means if there is a measured weight we
628        * use it in getWeight and return true hereWe assume only one weight.
629        * The logic here is to take the first found of measured or the first
630        * estimated if no measured found.  Equally valid would be to take the
631        * latest measurement, etc.. Sequential calls to getWeight(), getWeightUnits(),
632        * and getWeightMeasured() should return information about the same
633        * weight observation.  This might be un-threadsafe in environments where
634        * another thread could be writing a weight OBX while another is reading.
635        * In such cases, it would be better to get the OBX and extract the fields
636        * manually.<br>
637        * TODO: If demand arises, add a getWeightOBX() method to facilitate thread-safe access.
638        * @return String with weight units in ISO or ANSI abbreviation
639        */
640       public String getWeightUnits() {
641          String sWeightUnits = "";
642          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
643          Iterator i = loop.findOBXSegments(Loinc.BODY_WEIGHT_MEASURED);
644          if( i.hasNext() ) {
645             OBXSegment obx = (OBXSegment)i.next();
646             sWeightUnits = obx.getUnits().getValue();
647          }
648          else {
649             Iterator i2 = loop.findOBXSegments(Loinc.BODY_WEIGHT_ESTIMATED);
650             if( i2.hasNext() ) {
651                OBXSegment obx = (OBXSegment)i2.next();
652                sWeightUnits = obx.getUnits().getValue();
653             }
654          }
655          return sWeightUnits;
656       }
657    
658       /**
659        * Was the weight we returned with getWeight a measured weight rather
660        * than an estimate. <br><br>
661        * Note:  Thelogic used here must be kept in step with the logic used
662        * in getWeight().  For now that means if there is a measured weight we
663        * use it in getWeight and return true hereWe assume only one weight.
664        * The logic here is to take the first found of measured or the first
665        * estimated if no measured found.  Equally valid would be to take the
666        * latest measurement, etc.. Sequential calls to getWeight(), getWeightUnits(),
667        * and getWeightMeasured() should return information about the same
668        * weight observation.  This might be un-thread-safe in environments where
669        * one thread could be writing a weight OBX while another is reading.
670        * In such cases, it would be better to get the OBX and extract the fields
671        * manually.<br>
672        * TODO: If demand arises, add a getWeightOBX() method to facilitate thread-safe access.
673        * @return boolean true if a measured weight found.  NOTE: This returns false
674        * even if NO weight observation was found, so this should only be interpreted
675        * after retrieving a valid weight value.
676        */
677       public boolean getWeightMeasured() {
678          boolean bMeasured = false;
679          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
680          Iterator i = loop.findOBXSegments(Loinc.BODY_WEIGHT_MEASURED);
681          if( i.hasNext() ) {
682             bMeasured = true;
683          }
684          return bMeasured;
685       }
686    
687       /**
688        * Set the animal's color as uncoded text.<br><br>
689        * Stored as OBX with loinc code 29552-7 color nar
690        * @param sColor String color
691        */
692       public void setColor( String sColor ) {
693          // Need to construct OBX with appropriate LOINC code for measured or estimated
694          // weight observation.
695          OBXSegment obx = addOBX();
696          obx.setObservationIdentifier( Loinc.COLOR_NAR );
697          try {
698             obx.setObservationValueType( "ST" );
699             obx.setObservationValue( sColor );
700          }
701          catch( MalformedFieldException mfe ) {
702             mfe.printStackTrace();
703          }
704       }
705    
706       /**
707        * Get the animal's color as string.<br><br>
708        * Stored as OBX with loinc code 29552-7 color nar
709        * @return String color
710        */
711       public String getColor() {
712          String sColor = "";
713          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
714          Iterator i = loop.findOBXSegments(Loinc.COLOR_NAR);
715          if( i.hasNext() ) {
716             OBXSegment obx = (OBXSegment)i.next();
717             sColor = obx.getObservationValue().getValue();
718          }
719          return sColor;
720       }
721    
722       /**
723        * Set the microchip number and type.
724        * @param sChipNumber String with chip number
725        * @param sChipType String with chip type coded or text?
726        */
727       public void setMicrochip( String sChipNumber, String sChipType ) {
728          if( !Identifiers.isChipType( sChipType ) ) {
729             // Should replace this with more robust error handler.
730             // Real program should use pick list for chip type anyway.
731             System.err.println( "Unknown chip type: " + sChipType );
732          }
733          PIDSegment pid = getPID();
734          boolean bFound = false;
735          // First try to update existing chip entry (why would we ever?)
736          Iterator i = pid.listPatientIds();
737          while( i != null && i.hasNext() ) {
738             CXElement cxe = (CXElement)i.next();
739             // Check assigner logic
740             String sAssigner = cxe.getAssigningAuthorityName();
741             if( sAssigner != null && sAssigner.equals( sChipType ) ) {
742                cxe.setIdentifier( sChipNumber );
743                bFound = true;
744                break;
745             }
746          }
747          // Otherwise add a new identifier
748          if( !bFound ) {
749             pid.addPatientId( sChipNumber, sChipType );
750          }
751       }
752    
753       /**
754        * Get the microchip number.<br><br>
755        * Note: This logic assumes only one microchip per animal.  It is possible
756        * that an animal would have multiple, ie, one US and one European chip.
757        * More than one chip can be set using this program logic as long as the
758        * types differ.  They they will be stored and transmitted correctly and
759        * could be retrieved using lower-level code. However, this code returns only
760        * the first chip found.<br>
761        * Note: The same sort of thread-safety issue exists as in the
762        * {@link #getWeightUnits() getWeightUnits} method.
763        * @return String with chip number
764        */
765       public String getMicrochipNumber() {
766          PIDSegment pid = getPID();
767          // First try to update existing MRN
768          Iterator i = pid.listPatientIds();
769          while( i.hasNext() ) {
770             CXElement cxe = (CXElement)i.next();
771             // Check assigner logic
772             String sAssigner = cxe.getAssigningAuthorityName();
773             if( sAssigner != null && Identifiers.isChipType( sAssigner ) ) {
774                return cxe.getIdentifier();
775             }
776          }
777          return "";
778       }
779    
780       /**
781        * Get the microchip type.<br><br>
782        * Note: This logic assumes only one microchip per animal.  It is possible
783        * that an animal would have multiple, ie, one US and one European chip.
784        * More than one chip can be set using this program logic as long as the
785        * types differ.  They they will be stored and transmitted correctly and
786        * could be retrieved using lower-level code. However, this code returns only
787        * the first chip found.<br>
788        * Note: The same sort of thread-safety issue exists as in the
789        * {@link #getWeightUnits() getWeightUnits} method.
790        * @return String with chip type
791        */
792       public String getMicrochipType() {
793          PIDSegment pid = getPID();
794          // First try to update existing MRN
795          Iterator i = pid.listPatientIds();
796          while( i.hasNext() ) {
797             CXElement cxe = (CXElement)i.next();
798             // Check assigner logic
799             String sAssigner = cxe.getAssigningAuthorityName();
800             if( sAssigner != null && Identifiers.isChipType( sAssigner ) ) {
801                return sAssigner;
802             }
803          }
804          return "";
805       }
806    
807       /**
808        * Set the tattoo text and type.<br><br>
809        * Note: This logic assumes only one tattoo per animal.  It is possible
810        * that an animal would have multiple.  More than one tattoo can be set
811        * using this program logic as long as they are different types and they will
812        * be stored and transmitted correctly.
813        * However, this logic returns only the tattoo found.
814        * @param sTattooText String with text of tattoo
815        * @param sTattooType String with type of tattoo (location, program, etc.) The
816        * value supplied must be one of the constants defined in Identifiers.  Eventually
817        * these constants will be replaced with a more robust system for controlled
818        * vocabulary lookup.  For now, programmers may add constants to Identifiers,
819        * Loinc, etc., without being <i>required</i> to submit the change under the LGPL.
820        */
821       public void setTattoo( String sTattooText, String sTattooType ) {
822          if( !Identifiers.isTattooType( sTattooType ) ) {
823             // Should replace this with more robust error handler.
824             // Does a finite list of tattoo types make sense the same way microchip types do?
825             System.err.println( "Unknown tattoo type: " + sTattooType );
826          }
827          PIDSegment pid = getPID();
828          boolean bFound = false;
829          // First try to update existing chip entry (why would we ever?)
830          Iterator i = pid.listPatientIds();
831          while( i != null && i.hasNext() ) {
832             CXElement cxe = (CXElement)i.next();
833             // Check assigner logic
834             String sAssigner = cxe.getAssigningAuthorityName();
835             if( sAssigner != null && sAssigner.equals( sTattooType ) ) {
836                cxe.setIdentifier( sTattooText );
837                bFound = true;
838                break;
839             }
840          }
841          // Otherwise add a new identifier
842          if( !bFound ) {
843             pid.addPatientId( sTattooText, sTattooType );
844          }
845       }
846    
847       /**
848        * Get the tattoo text.<br><br>
849        * Note: This logic assumes only one tattoo per animal.  This method returns
850        * the first found.
851        * However, this logic returns only the tattoo found.<br>
852        * Note: The same sort of thread-safety issue exists as in the
853        * {@link #getWeightUnits() getWeightUnits} method.
854        * @return String with tattoo text
855        */
856       public String getTattooText() {
857          PIDSegment pid = getPID();
858          // First try to update existing MRN
859          Iterator i = pid.listPatientIds();
860          while( i.hasNext() ) {
861             CXElement cxe = (CXElement)i.next();
862             // Check assigner logic
863             String sAssigner = cxe.getAssigningAuthorityName();
864             if( sAssigner != null && Identifiers.isTattooType( sAssigner ) ) {
865                return cxe.getIdentifier();
866             }
867          }
868          return "";
869       }
870    
871       /**
872        * Get the tattoo type.<br><br>
873        * Note: This logic assumes only one tattoo per animal.  This method returns
874        * the first found.<br>
875        * Note: The same sort of thread-safety issue exists as in the
876        * {@link #getWeightUnits() getWeightUnits} method.
877        * @return String with tattoo type (location, program, etc.)
878        */
879       public String getTattooType() {
880          PIDSegment pid = getPID();
881          // First try to update existing MRN
882          Iterator i = pid.listPatientIds();
883          while( i.hasNext() ) {
884             CXElement cxe = (CXElement)i.next();
885             // Check assigner logic
886             String sAssigner = cxe.getAssigningAuthorityName();
887             if( sAssigner != null && Identifiers.isTattooType( sAssigner ) ) {
888                return sAssigner;
889             }
890          }
891          return "";
892       }
893    
894       /**
895        * Set the ear tag text and type.<br><br>
896        * Note: This logic assumes only one ear tag per animal.  It is possible
897        * that an animal would have multiple.  More than one ear tag can be set
898        * using this program logic as long as they are different types and they will
899        * be stored and transmitted correctly.
900        * However, this logic returns only the first ear tag found.<br>
901        * More so than the other identifiers, this may be an issue.  It is not
902        * a big project for the library developer to change to add/list logic but
903        * it will add complexity at the application programming layer as it will
904        * be up to the application to look through the list Iterator.
905        * @param sEarTagText String with text of ear tag
906        * @param sEarTagType String with type of ear tag (location, program, etc.)
907        */
908       public void setEarTag( String sEarTagText, String sEarTagType ) {
909          if( !Identifiers.isEarTagType( sEarTagType ) ) {
910             // Should replace this with more robust error handler.
911             // Real program should use pick list for chip type anyway.
912             System.err.println( "Unknown ear tag type: " + sEarTagType );
913          }
914          PIDSegment pid = getPID();
915          boolean bFound = false;
916          // First try to update existing chip entry (why would we ever?)
917          Iterator i = pid.listPatientIds();
918          while( i != null && i.hasNext() ) {
919             CXElement cxe = (CXElement)i.next();
920             // Check assigner logic
921             String sAssigner = cxe.getAssigningAuthorityName();
922             if( sAssigner != null && sAssigner.equals( sEarTagType ) ) {
923                cxe.setIdentifier( sEarTagText );
924                bFound = true;
925                break;
926             }
927          }
928          // Otherwise add a new identifier
929          if( !bFound ) {
930             pid.addPatientId( sEarTagText, sEarTagType );
931          }
932       }
933    
934       /**
935        * Get the ear tag text.<br><br>
936        * Note: This logic assumes only one ear tag per animal.  It returns
937        * only the first ear tag found.<br>
938        * Note: The same sort of thread-safety issue exists as in the
939        * {@link #getWeightUnits() getWeightUnits} method.
940        * @return String with ear tag text
941        */
942       public String getEarTagText() {
943          PIDSegment pid = getPID();
944          // First try to update existing MRN
945          Iterator i = pid.listPatientIds();
946          while( i.hasNext() ) {
947             CXElement cxe = (CXElement)i.next();
948             // Check assigner logic
949             String sAssigner = cxe.getAssigningAuthorityName();
950             if( sAssigner != null && Identifiers.isEarTagType( sAssigner ) ) {
951                return cxe.getIdentifier();
952             }
953          }
954          return "";
955       }
956    
957       /**
958        * Get the ear tag type.<br><br>
959        * Note: This logic assumes only one ear tag per animal.  It returns
960        * only the first ear tag found.<br>
961        * Note: The same sort of thread-safety issue exists as in the
962        * {@link #getWeightUnits() getWeightUnits} method.
963        * @return String with ear tag type
964        */
965       public String getEarTagType() {
966          PIDSegment pid = getPID();
967          // First try to update existing MRN
968          Iterator i = pid.listPatientIds();
969          while( i.hasNext() ) {
970             CXElement cxe = (CXElement)i.next();
971             // Check assigner logic
972             String sAssigner = cxe.getAssigningAuthorityName();
973             if( sAssigner != null && Identifiers.isEarTagType( sAssigner ) ) {
974                return sAssigner;
975             }
976          }
977          return "";
978       }
979    
980       /**
981        * Set the postal code of <strong>animal's</strong> residence.
982        * @param sPostalCode String with Zipcode or other postal code
983        */
984       public void setPatientZipcode( String sZipcode ) {
985          PIDSegment pid = getPID();
986          pid.setPatientZipcode( sZipcode );
987       }
988    
989       /**
990        * Get the postal code.
991        * @return String with Zipcode or other postal code
992        */
993       public String getPatientZipcode() {
994          PIDSegment pid = getPID();
995          return pid.getPatientZipcode();
996       }
997    
998       /**
999        * Set the patient's name (normally "Confidential").
1000        * @param sName String with name
1001        */
1002       public void setPatientName( String sName ) {
1003          PIDSegment pid = getPID();
1004          pid.setPatientName( sName );
1005       }
1006    
1007       /**
1008        * Get the patient's name.
1009        * @return String with Name
1010        */
1011       public String getPatientName() {
1012          PIDSegment pid = getPID();
1013          return pid.getPatientNameString();
1014       }
1015    
1016       /**
1017        * Set the patient class code (A, I, O, R).<br><br>
1018        * TODO: Add error checking to ensure only valud class code is provided.
1019        * @param sPatientClass String with patient class code
1020        */
1021       public void setPatientClass( String sPatClass ) {
1022          PV1Segment pv1 = getPV1();
1023          pv1.setPatientClass( sPatClass );
1024       }
1025    
1026       /**
1027        * Get the patient class code.
1028        * @return String with patient class code
1029        */
1030       public String getPatientClass() {
1031          PV1Segment pv1 = getPV1();
1032          return pv1.getPatientClass();
1033       }
1034    
1035       /**
1036        * Set the admission type code (C, K, E, Z, P, or R).<br><br>
1037        * TODO: Add error checking to ensure only valud class code is provided.
1038        * @param sAdmitType String with admission type code
1039        */
1040       public void setAdmissionType( String sAdmissionType ) {
1041          PV1Segment pv1 = getPV1();
1042          pv1.setAdmissionType( sAdmissionType );
1043       }
1044    
1045       /**
1046        * Get the admission type code.
1047        * @return String with admission type code
1048        */
1049       public String getAdmissionType() {
1050          PV1Segment pv1 = getPV1();
1051          return pv1.getAdmissionType();
1052       }
1053    
1054       /**
1055        * Set the attending doctor.
1056        * @param sIdNumber String Attending Doctor ID
1057        * @param sAssigningAuthority String with identifier of assigning facility
1058        */
1059       public void setAttendingDoctor( String sIdNumber, String sAssigningAuthority ) {
1060          PV1Segment pv1 = getPV1();
1061          pv1.setAttendingDoctorId( sIdNumber );
1062          // Check assigner logic
1063          pv1.setAttendingDoctorAssigningAuthority( sAssigningAuthority );
1064       }
1065    
1066       /**
1067        * Set the attending doctor ID number using the default assigning facility
1068        * of the facility sending the report.  (As is normal VMDB procedure,)
1069        * @param sIdNumber Attending Doctor identifier assigned by sending facility
1070        */
1071       public void setAttendingDoctorId( String sIdNumber ) {
1072          MSHSegment msh = getMSH();
1073          String sThisFacility = msh.getSendingFacilityName();
1074          setAttendingDoctor( sIdNumber, sThisFacility );
1075       }
1076    
1077       /**
1078        * Set the attending doctor.
1079        * @param sAssigningAuthority String with identifier of assigning facility
1080        */
1081       public void setAttendingDoctorAssigningAuthority( String sIdNumber, String sAssigningAuthority ) {
1082          PV1Segment pv1 = getPV1();
1083          // Check assigner logic
1084          pv1.setAttendingDoctorAssigningAuthority( sAssigningAuthority );
1085       }
1086    
1087       /**
1088        * Get the attending doctorn ID as a full XCN Element.<br><br>
1089        * Note: This and other "get" methods that return HL7 Element objects return
1090        * references to the object in place in the message.  They can thus be used to
1091        * obtain a reference to a newly added entry.  The XCNElement object's "set"
1092        * methods can then be used to add details not provided with mutator methods
1093        * at the message level.  These additional components will most likely be ignored
1094        * by VMDB when the message is received, but may be useful in other applications of the
1095        * same messages, and as standard HL7 content will not cause errors at VMDB.
1096        * @return Attending doctor's full extended composite name
1097        */
1098       public XCNElement getAttendingDoctor() {
1099          PV1Segment pv1 = getPV1();
1100          return pv1.getAttendingDoctor();
1101       }
1102    
1103       /**
1104        * Get the attending doctor ID number.
1105        * @return String with ID number
1106        */
1107       public String getAttendingDoctorId() {
1108          PV1Segment pv1 = getPV1();
1109          return pv1.getAttendingDoctorId();
1110       }
1111    
1112       /**
1113        * Get the attending doctor ID assigning facility.<br><br>
1114        * TODO: Check assigner logic and replace with Assigning Authority if
1115        * necessary.
1116        * @return String with identifier of Assigning Facility
1117        */
1118       public String getAttendingDoctorAssigningAuthority() {
1119          PV1Segment pv1 = getPV1();
1120          return pv1.getAttendingDoctorAssigningAuthority();
1121       }
1122    
1123       /**
1124        * Set the Visit Number.<br><br>
1125        * Normally assigned by the sending facility so this
1126        * is added as the default Assigning Faciltiy.
1127        * @param sVisitNumber String with facility-assigned visit identifier
1128        */
1129       public void setVisitNumber( String sVisitNumber ) {
1130          MSHSegment msh = getMSH();
1131          String sThisFacility = msh.getSendingFacilityName();
1132          PV1Segment pv1 = getPV1();
1133          pv1.setVisitNumberId( sVisitNumber );
1134          // Check assigner logic
1135          pv1.setVisitNumberAssigningAuthority( sThisFacility );
1136       }
1137    
1138       /**
1139        * Set the Visit Number ID assigning facility.<br><br>
1140        * Note: setVisitNumber() automatically assigns thisFacility as the assigning
1141        * facility.  So use only setVisitNumberAssigningAuthority() after setting
1142        * the number to override on those <i>rare</i> occasions when sending for a
1143        * subsidiary, etc., with a different identifier than the sender.<br>
1144        * TODO: Check assigner logic and replace with Assigning Authority if
1145        * necessary.
1146        * @param sAssigningAuthority String with namespace identifier for assigning facility
1147        */
1148       public void setVisitNumberAssigningAuthority( String sAssigningAuthority ) {
1149          PV1Segment pv1 = getPV1();
1150          pv1.setVisitNumberAssigningAuthority( sAssigningAuthority );
1151       }
1152    
1153       /**
1154        * Get the Visit Number ID as full CX Element.
1155        * @return CXElement with visit number
1156        */
1157       public CXElement getVisitNumber() {
1158          PV1Segment pv1 = getPV1();
1159          return pv1.getVisitNumber();
1160       }
1161    
1162       /**
1163        * Get the Visit Number ID number.
1164        * @return String with Visit ID number
1165        */
1166       public String getVisitNumberId() {
1167          PV1Segment pv1 = getPV1();
1168          return pv1.getVisitNumberId();
1169       }
1170    
1171       /**
1172        * Get the Visit Number assigning facility.<br><br>
1173        * TODO: Check assigner logic and replace with Assigning Authority if
1174        * necessary.
1175        * @return String with authority namespace id
1176        */
1177       public String getVisitNumberAssigningAuthority() {
1178          PV1Segment pv1 = getPV1();
1179          return pv1.getVisitNumberAssigningAuthority();
1180       }
1181    
1182       /**
1183        * Set the Discharge Disposition.<br><br>
1184        * TODO: Add code validation logic
1185        * @param sDisp one character string [0,1,2, or 3] for alive,
1186        * dead, euthanized, or referred
1187        */
1188       public void setDisposition( String sDisp ) {
1189          PV1Segment pv1 = getPV1();
1190          pv1.setDisposition( sDisp );
1191       }
1192    
1193       /**
1194        * Get Discharge Disposition string.
1195        * @return String with discharge disposition code
1196        */
1197       public String getDisposition() {
1198          PV1Segment pv1 = getPV1();
1199          return pv1.getDisposition();
1200       }
1201    
1202    
1203       /**
1204        * Set the admit date/time of the message to string formatted date/time.<br><br>
1205        * TODO: Add date format validation logic
1206        * @param sDateTime String with date/time in yyyyMMddHHmmss format
1207        *
1208        */
1209       public void setAdmitDateTime( String sDateTime ) {
1210          PV1Segment pv1 = getPV1();
1211          pv1.setAdmitDateTime( sDateTime );
1212       }
1213    
1214       /**
1215        * Get the admit date/time as a string.
1216        * @return String date/time
1217        */
1218       public String getAdmitDateTime() {
1219          PV1Segment pv1 = getPV1();
1220          return pv1.getAdmitDateTime();
1221       }
1222    
1223       /**
1224        * Set the discharge date/time of the message to string formatted date/time.<br><br>
1225        * TODO: Add date format validation logic
1226        * @param sDateTime String with date/time in yyyyMMddHHmmss format
1227        *
1228        */
1229       public void setDischargeDateTime( String sDateTime ) {
1230          PV1Segment pv1 = getPV1();
1231          pv1.setDischargeDateTime( sDateTime );
1232       }
1233    
1234       /**
1235        * Get the discharge date/time as a string.
1236        * @return String date/time in yyyyMMddHHmmss format
1237        */
1238       public String getDischargeDateTime() {
1239          PV1Segment pv1 = getPV1();
1240          return pv1.getDischargeDateTime();
1241       }
1242    
1243    
1244       /**
1245        * Set the Order Control.<br><br>
1246        * TODO: Add code validation logic
1247        * @param sOrder Control NW, CA, RP, or RO,
1248        */
1249       public void setOrderControl( String sOrderControl ) {
1250          ORCSegment orc = getORC();
1251          orc.setOrderControl( sOrderControl );
1252       }
1253    
1254       /**
1255        * Get Order Contrl string.
1256        * @return String with discharge disposition code
1257        */
1258       public String getOrderControl() {
1259          ORCSegment orc = getORC();
1260          return orc.getOrderControl();
1261       }
1262    
1263       /**
1264        * Set the coded universal service identifier.<br><br>
1265        * This version takes the fully qualified CEElement components
1266        * @param sCode code value normally snomed
1267        * @param sText spelled out term
1268        * @param sSystem normally SCT
1269        */
1270       public void setUniversalServiceId( String sCode, String sText, String sCodeType ) {
1271          CEElement ceE = new CEElement( sCode, sText, sCodeType );
1272          OBRSegment obr = getOBR();
1273          obr.setUniversalServiceId(ceE);
1274       }
1275    
1276       /**
1277        * Set the coded universal service identifier as a preformed CE Element.<br><br>
1278        * Note: For VMDB submissions this will always be called as <br>
1279        * <code>setUniversalServiceId( Loinc.CHART_ABSTRACT );</code><br>
1280        * If we can figure out where to put it, we may make this a default.  The
1281        * problem with that would be confusion when used for other applications.
1282        * @param ceServiceId CEElement preformed usually constant from LOINC or SNOMED
1283        */
1284       public void setUniversalServiceId( CEElement ceServiceId ) {
1285          OBRSegment obr = getOBR();
1286          obr.setUniversalServiceId(ceServiceId);
1287       }
1288    
1289          /**
1290           * Get the coded universal service identifier as a preformed CE Element.
1291           * @return A reference to a CEElement with the Universal Service ID.
1292           * Normally a preformed usually constant from LOINC or SNOMED
1293           */
1294       public CEElement getUniversalServiceId() {
1295          OBRSegment obr = getOBR();
1296          return obr.getUniversalServiceId();
1297       }
1298    
1299          /**
1300           * Get the universal service identifier text.
1301           * @return String text of the type of service "requested."  In VMDB submissions
1302           * this is always "Chart Abstract"
1303           */
1304       public String getUniversalServiceIdText() {
1305          OBRSegment obr = getOBR();
1306          return obr.getUniversalServiceIdText();
1307       }
1308    
1309    
1310       /**
1311        * Set the observation date/time of the message to string formatted date/time.
1312        * @param sDateTime String with date/time in yyyyMMddHHmmss format
1313        */
1314       public void setObservationDateTime( String sDateTime ) {
1315          OBRSegment obr = getOBR();
1316          obr.setObservationDateTime( sDateTime );
1317       }
1318    
1319       /**
1320        * Get the observation date/time as a string.
1321        * @return String date/time
1322        */
1323       public String getObservationDateTime() {
1324          OBRSegment obr = getOBR();
1325          return obr.getObservationDateTime();
1326       }
1327    
1328       /**
1329        * Modify a coded OBXSegment using predefined Coded Entry for identifier and code.<br><br>
1330        * Often uses one of the set of predefined constants.<br>
1331        * The same Modifier methods can be used for Complaints, Diagnoses, Procedures, Findings, etc.,
1332        * anything in a coded OBX.<br>
1333        * This logic uses the SubId field value.  Each root observation within a GRP_1
1334        * loop receives a unique integer SubId.  Modifiers of that root receive the root's
1335        * integer followed by a period and a sequential integer.  Modifiers of modifiers
1336        * similarly add another period and integer.<br>
1337        * TODO: Get the correct nomenclature for ceIdentifier and adjust method and
1338        * documentation accordingly.
1339        * @param obxRoot The OBXSegment to which modifier is applied.  Used to
1340        * obtain SubId value for this segment.
1341        * @param ceIdentifier CEElement using predefined snomed code for type of
1342        * modification such as "with associated etiology" or "with laterality."
1343        * @param ceModifier CEElement populated with modifier term.
1344        */
1345       public OBXSegment modifyObservation( OBXSegment obxRoot,
1346                                            CEElement ceIdentifier,
1347                                            CEElement ceModifier ) {
1348          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
1349          String sSubId = loop.nextModifierSubId( obxRoot );
1350          // Don't allow automatic assignment of top-level subId
1351          OBXSegment obx = loop.addOBX( sSubId );
1352          obx.setObservationIdentifier( ceIdentifier );
1353          try {
1354             obx.setObservationValueType( "CE" );
1355             obx.setObservationValue( ceModifier );
1356          }
1357          catch( MalformedFieldException mfe ) {
1358             mfe.printStackTrace();
1359          }
1360          return obx;
1361       }
1362    
1363       /**
1364        * Get the Modifier list as iterator over {@link org.vmdb.hl7.OBXSegment OBXSegment} objects.<br><br>
1365        * The implementation is going to be very Java-specific because it depends on garbage collection
1366        * to maintain an instance of a Vector until Iterator is released.
1367        * @param obxRoot The OBXSegment to which modifiers are applied.  Used to
1368        * obtain subId value for this segment.  This does not need to be a root-level
1369        * diagnosis, finding, etc..  It can be itself a modifier.  It simply indicates the
1370        * observation being modified by the modifiers returned.  Similarly, modifiers of
1371        * these modifiers are not returned.  To retrieve them, call listModifiers again
1372        * passign each of the OBXs in the iterator in turn.  To retrieve all modifiers
1373        * that affect this observation directly or indirectly, you would perform a
1374        * depth-first recursive tree walk, but that--as they say--is an excercise left
1375        * to the student.
1376        * @return Iterator over collection of {@link org.vmdb.hl7.OBXSegment OBXSegment}
1377        * objects that modify the root OBX.
1378        */
1379       public Iterator listModifiers( OBXSegment obxRoot ) {
1380          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
1381          Iterator i = loop.findOBXModifiers( obxRoot );
1382          return i;
1383       }
1384    
1385       /**
1386        * Set the Chief Complaint as a predefined CE Element.
1387        * Often uses one of the set of predefined constants.
1388        * @param ceChiefComplaint CEElement populated with Chief Complaint
1389        * @return OBXSegment just added.  This is normally used
1390        * to apply modifiers if needed.
1391        */
1392       public OBXSegment setChiefComplaint( CEElement ceChiefComplaint ) {
1393          OBXSegment obx = addOBX();
1394          obx.setObservationIdentifier( Loinc.CHIEF_COMPLAINT_NOM );
1395          try {
1396             obx.setObservationValueType( "CE" );
1397             obx.setObservationValue( ceChiefComplaint );
1398          }
1399          catch( MalformedFieldException mfe ) {
1400             mfe.printStackTrace();
1401          }
1402          return obx;
1403       }
1404    
1405       /**
1406        * Set the Chief Complaint as the individual components of a CE Element.
1407        * @param sChiefComplaintCode String snomed code for Chief Complaint
1408        * @param sChiefComplaintText String Chief Complaint spelled out
1409        * @param sCodeSystem String, usually SCT, with type of code
1410        * @return OBXSegment just added.  This is normally used
1411        * to apply modifiers if needed.
1412        */
1413       public OBXSegment setChiefComplaint( String sChiefComplaintCode,
1414                                            String sChiefComplaintText,
1415                                            String sCodeSystem ) {
1416          // Need to construct OBX with appropriate LOINC code for measured or estimated
1417          // weight observation.
1418          OBXSegment obx = addOBX();
1419          obx.setObservationIdentifier( Loinc.CHIEF_COMPLAINT_NOM );
1420          CEElement ceE = new CEElement();
1421          try {
1422             ceE.setIdentifier( sChiefComplaintCode );
1423             ceE.setText( sChiefComplaintText );
1424             ceE.setCodingSystem( sCodeSystem );
1425             obx.setObservationValueType( "CE" );
1426             obx.setObservationValue( ceE );
1427          }
1428          catch( MalformedFieldException mfe ) {
1429             mfe.printStackTrace();
1430          }
1431          return obx;
1432       }
1433    
1434       /**
1435        * Set the Chief Complaint as the individual components of a CE Element.
1436        * @param sChiefComplaintCode String snomed code for Chief Complaint
1437        * @param sChiefComplaintText String Chief Complaint spelled out
1438        * @param sCodeSystem String, usually SCT, with type of code
1439        * @param sAltChiefComplaintCode String alternate code for the SAME Chief Complaint
1440        * @param sAltChiefComplaintText String Chief Complaint spelled out as in alternate system
1441        * @param sAltCodeSystem String, alternate type of code
1442        * @return OBXSegment just added.  This is normally used
1443        * to apply modifiers if needed.
1444        */
1445       public OBXSegment setChiefComplaint( String sChiefComplaintCode,
1446                                            String sChiefComplaintText,
1447                                            String sCodeSystem ,
1448                                            String sAltChiefComplaintCode,
1449                                            String sAltChiefComplaintText,
1450                                            String sAltCodeSystem) {
1451          // Need to construct OBX with appropriate LOINC code for measured or estimated
1452          // weight observation.
1453          OBXSegment obx = addOBX();
1454          obx.setObservationIdentifier( Loinc.CHIEF_COMPLAINT_NOM );
1455          CEElement ceE = new CEElement();
1456          try {
1457             ceE.setIdentifier( sChiefComplaintCode );
1458             ceE.setText( sChiefComplaintText );
1459             ceE.setCodingSystem( sCodeSystem );
1460             ceE.setAltIdentifier( sAltChiefComplaintCode );
1461             ceE.setAltText( sAltChiefComplaintText );
1462             ceE.setAltCodingSystem( sAltCodeSystem );
1463             obx.setObservationValueType( "CE" );
1464             obx.setObservationValue( ceE );
1465          }
1466          catch( MalformedFieldException mfe ) {
1467             mfe.printStackTrace();
1468          }
1469          return obx;
1470       }
1471    
1472       /**
1473        * Get the Chief Complaint as a predefined CE Element.<br><br>
1474        * Often uses one of the set of predefined constants.
1475        * @return CEElement populated with ChiefComplaint
1476        */
1477       public CEElement getChiefComplaint() {
1478          CEElement ceChiefComplaint = null;
1479          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
1480          Iterator i = loop.findOBXSegments( Loinc.CHIEF_COMPLAINT_NOM );
1481          if( i.hasNext() ) {
1482             OBXSegment obx = (OBXSegment)i.next();
1483             if( obx.getObservationValueType().equals( "CE" ) ) {
1484                ceChiefComplaint = (CEElement)obx.getObservationValue();
1485             }
1486             else { // For now we'll assume it is a simple type.  If not, we'll get the whole HL7
1487                ceChiefComplaint = new CEElement();
1488                ceChiefComplaint.setText( obx.getObservationValue().getValue() );
1489             }
1490          }
1491          return ceChiefComplaint;
1492       }
1493    
1494       /**
1495        * Get the Chief Complaint text.
1496        * @return String ChiefComplaint spelled out
1497        */
1498       public String getChiefComplaintText() {
1499          CEElement ceChiefComplaint = getChiefComplaint();
1500          if( ceChiefComplaint == null ) return "";
1501          return ceChiefComplaint.getText();
1502       }
1503    
1504       /**
1505        * Add a Diagnosis as a predefined CE Element.<br><br>
1506        * Often uses one of the set of predefined constants
1507        * @param ceDiagnosis CEElement populated with the coded Diagnosis
1508        * @return OBXSegment just added.  This is normally used
1509        * to apply modifiers if needed.
1510        */
1511       public OBXSegment addDiagnosis( CEElement ceDiagnosis ) {
1512          return addDiagnosis( ceDiagnosis, false );
1513       }
1514    
1515       /**
1516        * Add a Diagnosis as a predefined CE Element.<br><br>
1517        * Often uses one of the set of predefined constants
1518        * @param ceDiagnosis CEElement populated with the coded Diagnosis
1519        * @param bRecheck true if this diagnosis is a recheck (default false)
1520        * @return OBXSegment just added.  This is normally used
1521        * to apply modifiers if needed.
1522        */
1523       public OBXSegment addDiagnosis( CEElement ceDiagnosis, boolean bRecheck ) {
1524          OBXSegment obx = addOBX();
1525          if( bRecheck )
1526             obx.setObservationIdentifier( Loinc.RECHECK_NOM );
1527          else
1528             obx.setObservationIdentifier( Loinc.DIAGNOSIS_NOM );
1529          try {
1530             obx.setObservationValueType( "CE" );
1531             obx.setObservationValue( ceDiagnosis );
1532          }
1533          catch( MalformedFieldException mfe ) {
1534             mfe.printStackTrace();
1535          }
1536          return obx;
1537       }
1538    
1539       /**
1540        * Add a Diagnosis as the individual components of the CE Element.
1541        * @param sDiagnosisCode String snomed code for the Diagnosis
1542        * @param sDiagnosisText String with the Diagnosis spelled out
1543        * @param sCodeSystem String, usually "SCT," with the type of code used
1544        * @param bRecheck true if this diagnosis is a recheck (default false)
1545        * @return OBXSegment just added.  This is normally used
1546        * to apply modifiers if needed.
1547        */
1548       public OBXSegment addDiagnosis( String sDiagnosisCode,
1549                                       String sDiagnosisText,
1550                                       String sCodeSystem ) {
1551          return addDiagnosis( sDiagnosisCode, sDiagnosisText, sCodeSystem, false );
1552       }
1553    
1554       /**
1555        * Add a Diagnosis as the individual components of the CE Element.
1556        * @param sDiagnosisCode String snomed code for the Diagnosis
1557        * @param sDiagnosisText String with the Diagnosis spelled out
1558        * @param sCodeSystem String, usually "SCT," with the type of code used
1559        * @param sAltDiagnosisCode String alternate code for the Diagnosis
1560        * @param sAltDiagnosisText String with the Diagnosis spelled out as in alternate system
1561        * @param sAltCodeSystem String, alternate type of code used
1562        * @param bRecheck true if this diagnosis is a recheck (default false)
1563        * @return OBXSegment just added.  This is normally used
1564        * to apply modifiers if needed.
1565        */
1566       public OBXSegment addDiagnosis( String sDiagnosisCode,
1567                                       String sDiagnosisText,
1568                                       String sCodeSystem,
1569                                       String sAltDiagnosisCode,
1570                                       String sAltDiagnosisText,
1571                                       String sAltCodeSystem ) {
1572          return addDiagnosis( sDiagnosisCode, sDiagnosisText, sCodeSystem,
1573                               sAltDiagnosisCode, sAltDiagnosisText, sAltCodeSystem, false );
1574       }
1575    
1576          /**
1577           * Add a Diagnosis as the individual components of the CE Element.
1578           * @param sDiagnosisCode String snomed code for the Diagnosis
1579           * @param sDiagnosisText String with the Diagnosis spelled out
1580           * @param sCodeSystem String, usually "SCT," with the type of code used
1581           * @param bRecheck true if this diagnosis is a recheck (default false)
1582           * @return OBXSegment just added.  This is normally used
1583           * to apply modifiers if needed.
1584           */
1585       public OBXSegment addDiagnosis( String sDiagnosisCode,
1586                                       String sDiagnosisText,
1587                                       String sCodeSystem,
1588                                       boolean bRecheck ) {
1589          OBXSegment obx = addOBX();
1590          if( bRecheck )
1591             obx.setObservationIdentifier( Loinc.RECHECK_NOM );
1592          else
1593             obx.setObservationIdentifier( Loinc.DIAGNOSIS_NOM );
1594          CEElement ceE = new CEElement();
1595          try {
1596             ceE.setIdentifier( sDiagnosisCode );
1597             ceE.setText( sDiagnosisText );
1598             ceE.setCodingSystem( sCodeSystem );
1599             obx.setObservationValueType( "CE" );
1600             obx.setObservationValue( ceE );
1601          }
1602          catch( MalformedFieldException mfe ) {
1603             mfe.printStackTrace();
1604          }
1605          return obx;
1606       }
1607    
1608          /**
1609           * Add a Diagnosis as the individual components of the CE Element.
1610           * @param sDiagnosisCode String snomed code for the Diagnosis
1611           * @param sDiagnosisText String with the Diagnosis spelled out
1612           * @param sCodeSystem String, usually "SCT," with the type of code used
1613           * @param sAltDiagnosisCode String alternate code for the Diagnosis
1614           * @param sAltDiagnosisText String with the Diagnosis spelled out as in alternate system
1615           * @param sAltCodeSystem String, Alternate coding system
1616           * @param bRecheck true if this diagnosis is a recheck (default false)
1617           * @return OBXSegment just added.  This is normally used
1618           * to apply modifiers if needed.
1619           */
1620       public OBXSegment addDiagnosis( String sDiagnosisCode,
1621                                       String sDiagnosisText,
1622                                       String sCodeSystem,
1623                                       String sAltDiagnosisCode,
1624                                       String sAltDiagnosisText,
1625                                       String sAltCodeSystem,
1626                                       boolean bRecheck ) {
1627          OBXSegment obx = addOBX();
1628          if( bRecheck )
1629             obx.setObservationIdentifier( Loinc.RECHECK_NOM );
1630          else
1631             obx.setObservationIdentifier( Loinc.DIAGNOSIS_NOM );
1632          CEElement ceE = new CEElement();
1633          try {
1634             ceE.setIdentifier( sDiagnosisCode );
1635             ceE.setText( sDiagnosisText );
1636             ceE.setCodingSystem( sCodeSystem );
1637             ceE.setAltIdentifier( sAltDiagnosisCode );
1638             ceE.setAltText( sAltDiagnosisText );
1639             ceE.setAltCodingSystem( sAltCodeSystem );
1640             obx.setObservationValueType( "CE" );
1641             obx.setObservationValue( ceE );
1642          }
1643          catch( MalformedFieldException mfe ) {
1644             mfe.printStackTrace();
1645          }
1646          return obx;
1647       }
1648    
1649       /**
1650        * Get the Diagnosis list as an iterator over {@link org.vmdb.hl7.OBXSegment OBXSegment} objects.<br><br>
1651        * Often uses one of the set of predefined constants.<br>
1652        * The implementation is going to be very Java-specific because it depends on garbage collection
1653        * to maintain an instance of a Vector until Iterator is released.
1654        * @return Iterator  over collection
1655        * of {@link org.vmdb.hl7.OBXSegment OBXSegment} objects from Diagnosis OBXs
1656        */
1657       public Iterator listDiagnoses() {
1658          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
1659          Iterator i = loop.findOBXSegments( Loinc.DIAGNOSIS_NOM );
1660          return i;
1661       }
1662    
1663       /**
1664        * Get the Recheck list as an iterator over {@link org.vmdb.hl7.OBXSegment OBXSegment} objects.<br><br>
1665        * Often uses one of the set of predefined constants.<br>
1666        * The implementation is going to be very Java-specific because it depends on garbage collection
1667        * to maintain an instance of a Vector until Iterator is released.
1668        * @return Iterator  over collection
1669        * of {@link org.vmdb.hl7.OBXSegment OBXSegment} objects from Recheck OBXs
1670        */
1671       public Iterator listRechecks() {
1672          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
1673          Iterator i = loop.findOBXSegments( Loinc.RECHECK_NOM );
1674          return i;
1675       }
1676    
1677       /**
1678        * Add a Procedure as a predefined CE Element.<br><br>
1679        * Often uses one of the set of predefined constants
1680        * @param ceProcedure CEElement populated with the coded Procedure
1681        * @return OBXSegment just added.  This is normally used
1682        * to apply modifiers if needed.
1683        */
1684       public OBXSegment addProcedure( CEElement ceProcedure ) {
1685          OBXSegment obx = addOBX();
1686          obx.setObservationIdentifier( Loinc.PROCEDURE_TYPE );
1687          try {
1688             obx.setObservationValueType( "CE" );
1689             obx.setObservationValue( ceProcedure );
1690          }
1691          catch( MalformedFieldException mfe ) {
1692             mfe.printStackTrace();
1693          }
1694          return obx;
1695       }
1696    
1697       /**
1698        * Add a Procedure as the individual components of a CE Element.
1699        * @param sProcedureCode String with the code for Procedure
1700        * @param sProcedureText String with the Procedure spelled out
1701        * @param sCodeSystem String, usually "SCT," with the type of code
1702        * @return OBXSegment just added.  This is normally used
1703        * to apply modifiers if needed.
1704        */
1705       public OBXSegment addProcedure( String sProcedureCode,
1706                                       String sProcedureText,
1707                                       String sCodeSystem ) {
1708          OBXSegment obx = addOBX();
1709          obx.setObservationIdentifier( Loinc.PROCEDURE_TYPE );
1710          CEElement ceE = new CEElement();
1711          try {
1712             ceE.setIdentifier( sProcedureCode );
1713             ceE.setText( sProcedureText );
1714             ceE.setCodingSystem( sCodeSystem );
1715             obx.setObservationValueType( "CE" );
1716             obx.setObservationValue( ceE );
1717          }
1718          catch( MalformedFieldException mfe ) {
1719             mfe.printStackTrace();
1720          }
1721          return obx;
1722       }
1723    
1724       /**
1725        * Add a Procedure as the individual components of a CE Element.
1726        * @param sProcedureCode String with the code for Procedure
1727        * @param sProcedureText String with the Procedure spelled out
1728        * @param sCodeSystem String, usually "SCT," with the type of code
1729        * @param sAltProcedureCode String alternate code for the SAME Procedure
1730        * @param sAltProcedureText String with the Procedure spelled out as in the alternate system
1731        * @param sAltCodeSystem String, alternate type of code
1732        * @return OBXSegment just added.  This is normally used
1733        * to apply modifiers if needed.
1734        */
1735       public OBXSegment addProcedure( String sProcedureCode,
1736                                       String sProcedureText,
1737                                       String sCodeSystem ,
1738                                       String sAltProcedureCode,
1739                                       String sAltProcedureText,
1740                                       String sAltCodeSystem) {
1741          OBXSegment obx = addOBX();
1742          obx.setObservationIdentifier( Loinc.PROCEDURE_TYPE );
1743          CEElement ceE = new CEElement();
1744          try {
1745             ceE.setIdentifier( sProcedureCode );
1746             ceE.setText( sProcedureText );
1747             ceE.setCodingSystem( sCodeSystem );
1748             ceE.setAltIdentifier( sAltProcedureCode );
1749             ceE.setAltText( sAltProcedureText );
1750             ceE.setAltCodingSystem( sAltCodeSystem );
1751             obx.setObservationValueType( "CE" );
1752             obx.setObservationValue( ceE );
1753          }
1754          catch( MalformedFieldException mfe ) {
1755             mfe.printStackTrace();
1756          }
1757          return obx;
1758       }
1759    
1760       /**
1761        * Get the Procedure list as an iterator over {@link org.vmdb.hl7.OBXSegment OBXSegment} objects.<br><br>
1762        * Often uses one of the set of predefined constants.<br>
1763        * The implementation is going to be very Java-specific because it depends on garbage collection
1764        * to maintain an instance of a Vector until Iterator is released.
1765        * @return Iterator over collection of {@link org.vmdb.hl7.OBXSegment OBXSegment} objects from Procedure OBXs
1766        */
1767       public Iterator listProcedures() {
1768          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
1769          Iterator i = loop.findOBXSegments( Loinc.PROCEDURE_TYPE );
1770          return i;
1771       }
1772    
1773       /**
1774        * Add a Finding as a predefined CE Element.<br><br>
1775        * Often uses one of the set of predefined constants
1776        * @param ceFinding CEElement populated with the coded Finding
1777        * @return OBXSegment just added.  This is normally used
1778        * to apply modifiers if needed.
1779        */
1780       public OBXSegment addFinding( CEElement ceFinding ) {
1781          OBXSegment obx = addOBX();
1782          obx.setObservationIdentifier( Loinc.FINDINGS_NOM );
1783          try {
1784             obx.setObservationValueType( "CE" );
1785             obx.setObservationValue( ceFinding );
1786          }
1787          catch( MalformedFieldException mfe ) {
1788             mfe.printStackTrace();
1789          }
1790          return obx;
1791       }
1792    
1793       /**
1794        * Add a Finding as the individual components of a CE Element.
1795        * @param sFindingCode String with the code for Finding
1796        * @param sFindingText String with the Finding spelled out
1797        * @param sCodeSystem String, usually "SCT," with the type of code used
1798        * @return OBXSegment just added.  This is normally used
1799        * to apply modifiers if needed.
1800        */
1801       public OBXSegment addFinding( String sFindingCode,
1802                                     String sFindingText,
1803                                     String sCodeSystem ) {
1804          OBXSegment obx = addOBX();
1805          obx.setObservationIdentifier( Loinc.FINDINGS_NOM );
1806          CEElement ceE = new CEElement();
1807          try {
1808             ceE.setIdentifier( sFindingCode );
1809             ceE.setText( sFindingText );
1810             ceE.setCodingSystem( sCodeSystem );
1811             obx.setObservationValueType( "CE" );
1812             obx.setObservationValue( ceE );
1813          }
1814          catch( MalformedFieldException mfe ) {
1815             mfe.printStackTrace();
1816          }
1817          return obx;
1818       }
1819    
1820       /**
1821        * Add a Finding as the individual components of a CE Element.
1822        * @param sFindingCode String with the code for Finding
1823        * @param sFindingText String with the Finding spelled out
1824        * @param sCodeSystem String, usually "SCT," with the type of code used
1825        * @param sAltFindingCode String alternate code for the SAME Finding
1826        * @param sAltFindingText String with the Finding spelled out
1827        * @param sAltCodeSystem String, aternate type of code used
1828        * @return OBXSegment just added.  This is normally used
1829        * to apply modifiers if needed.
1830        */
1831       public OBXSegment addFinding( String sFindingCode,
1832                                     String sFindingText,
1833                                     String sCodeSystem ,
1834                                     String sAltFindingCode,
1835                                     String sAltFindingText,
1836                                     String sAltCodeSystem) {
1837          OBXSegment obx = addOBX();
1838          obx.setObservationIdentifier( Loinc.FINDINGS_NOM );
1839          CEElement ceE = new CEElement();
1840          try {
1841             ceE.setIdentifier( sFindingCode );
1842             ceE.setText( sFindingText );
1843             ceE.setCodingSystem( sCodeSystem );
1844             ceE.setAltIdentifier( sAltFindingCode );
1845             ceE.setAltText( sAltFindingText );
1846             ceE.setAltCodingSystem( sAltCodeSystem );
1847             obx.setObservationValueType( "CE" );
1848             obx.setObservationValue( ceE );
1849          }
1850          catch( MalformedFieldException mfe ) {
1851             mfe.printStackTrace();
1852          }
1853          return obx;
1854       }
1855    
1856       /**
1857        * Get the Finding list as an iterator over {@link org.vmdb.hl7.OBXSegment OBXSegment} objects.<br><br>
1858        * Often uses one of the set of predefined constants.<br>
1859        * The implementation is going to be very Java-specific because it depends on garbage collection
1860        * to maintain an instance of a Vector until Iterator is released.
1861        * @return Iterator  over collection
1862        * of {@link org.vmdb.hl7.OBXSegment OBXSegment} objects from Finding OBXs
1863        */
1864       public Iterator listFindings() {
1865          ORDER_OBSERVATIONLoop loop = getPATIENT_RESULT().getORDER_OBSERVATION();
1866          Iterator i = loop.findOBXSegments( Loinc.FINDINGS_NOM );
1867          return i;
1868       }
1869    
1870    } // end class ORUMessage
1871