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