001 package org.vmdb.hl7; 002 003 import java.util.*; 004 005 /** 006 * <p><Title:> Generic HL7 Message Structure. </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>This almost virtual base class provides the common structure and 011 * functionality for all implemented specific messages. By using this class 012 * directly it is possible to build messages of types not yet implemented, but 013 * at considerably greater effort than using an implemented specific class. If 014 * you do build specific messages, you are encouraged to construct a specific 015 * subclass and submit it to VMDB for inclusion in the library. While you are not 016 * required to so extend the library, if you do implement such an extension, you 017 * are required by the LGPL to submit the extension. And this is the whole 018 * spirit of this project. Your application code is yours to keep proprietary. 019 * The library is provided as a tool to keep us all building to the same standards 020 * and interpretation of the standards. So to ensure interoperability, it is in 021 * everyone's interest that you abide by this requirement.</p> 022 * <p>Much of the functionality we would expect to find in this class is actually 023 * inherited from the HL7SegmentContainer because parsing a Message is really 024 * no different from parsing a "loop."</p> 025 * @author Michael K. Martin 026 * @version 1.0 027 */ 028 029 public class HL7Message extends HL7SegmentContainer { 030 private String sDTD; 031 private String sStyleSheet = null; 032 033 public HL7Message() { 034 vSegments = new Vector(); 035 } 036 037 /** 038 * Add a segment already instantiated as an object of type HL7Segment or specific subclass 039 * @param sSegment HL7Segment object 040 * @return true if successful 041 */ 042 boolean addSegment( HL7Segment sSegment ) { 043 if( sSegment != null ) { 044 if( sSegment instanceof MSHSegment ) { 045 setName( ((MSHSegment)sSegment).getMessageName() ); 046 setSeparators( ((MSHSegment)sSegment).getSeparators() ); 047 } 048 vSegments.add( sSegment ); 049 return true; 050 } 051 else 052 return false; 053 } 054 055 /** 056 * Parse the supplied HL7 message string into segments based upon the 057 * rule string provided by derived class of message or loop.<br><br> 058 * @param sHL7 String with HL7 message or fragement starting with this 059 * message or loop. 060 */ 061 public boolean readHL7( String sHL7 ) 062 { 063 vSegments.clear(); 064 return readString( sHL7 ); 065 } 066 067 /** 068 * Return the existing MSH segment if it exists or create and insert 069 * it at the correct location and return it. 070 * @return MSHSegment reference existing or new. 071 */ 072 public MSHSegment getMSH() { 073 MSHSegment msh = (MSHSegment)findSegment( "MSH" ); 074 if( msh == null ) { 075 msh = new MSHSegment(); 076 msh.initialize(); 077 if( vSegments == null ) vSegments = new Vector(); 078 vSegments.add(0,msh); 079 } 080 return msh; 081 } 082 083 /** 084 * Set the DTD file or URL.<br><br> 085 * This cannot be automatic because different implementations my 086 * locate DTD differently. 087 * @param sDTD fully qualified file or URL 088 */ 089 public void setDTD( String sDTD ) { 090 this.sDTD = sDTD; 091 } 092 093 /** 094 * Get the URL encoded DTD string. 095 * @return String with fully qualified DTD URL 096 */ 097 public String getDTD() { return sDTD; } 098 099 100 /** 101 * Set the StyleSheet file or URL.<br><br> 102 * This cannot be automatic because different implementations my 103 * locate StyleSheet differently. 104 * @param sStyleSheet fully qualified file or URL 105 */ 106 public void setStyleSheet( String sStyleSheet ) { 107 this.sStyleSheet = sStyleSheet; 108 } 109 110 /** 111 * Get the URL encoded StyleSheet string 112 * @return String with fully qualified Stylesheet URL 113 */ 114 public String getStyleSheet() { return sStyleSheet; } 115 116 /** 117 * Set the message name and event.<br><br> 118 * Normally this is called by the constructor using constants defined for 119 * this message. May be called directly if building a message of a type 120 * not yet implemented in a specific subclass. 121 * @param sMsgType String with message type abbreviation such as "ORU" 122 * @param sEventType String with message event such as "R01" 123 */ 124 public void setMessageName( String sMsgType, String sEventType ) { 125 MSHSegment msh = getMSH(); 126 String sMsgStructure = sMsgType + "_" + sEventType; 127 msh.setMessageName( sMsgType, sEventType, sMsgStructure ); 128 } 129 130 /** 131 * Set the message name, event, and structure.<br><br> 132 * Normally this is called by the constructor using constants defined for 133 * this message. May be called directly if building a message of a type 134 * not yet implemented in a specific subclass.<br> 135 * Message structure is often but not always name_event. Where messages 136 * for various events share structure, they use the first event. 137 * @param sMsgType String with message type abbreviation such as "ORU" 138 * @param sEventType String with message event such as "R01" 139 * @param sMsgStructure String with message event such as "ORU_R01" 140 */ 141 public void setMessageName( String sMsgType, String sEventType, String sMsgStructure ) { 142 MSHSegment msh = getMSH(); 143 msh.setMessageName( sMsgType, sEventType, sMsgStructure ); 144 } 145 146 /** 147 * Get the message name. 148 * @return String with message name string in form type_version 149 */ 150 public String getMessageName() { 151 MSHSegment msh = getMSH(); 152 return msh.getMessageName(); 153 } 154 155 /** 156 * Set the message control id. 157 * @param sMessageId String with unique identifier 158 */ 159 public void setMessageControlId( String sMessageControlId ) { 160 MSHSegment msh = getMSH(); 161 msh.setMessageControlId( sMessageControlId ); 162 } 163 164 /** 165 * Get the message control id. 166 * @return String with unique identifier 167 */ 168 public String getMessageControlId() { 169 MSHSegment msh = getMSH(); 170 return msh.getMessageControlId(); 171 } 172 173 /** 174 * Set the Sending facility VMDB Institution Id. 175 * @param sName String with VMDB facility name 176 * @param sId String with VMDB facility id 177 */ 178 public void setSendingFacility( HDElement hdIn ) { 179 MSHSegment msh = getMSH(); 180 msh.setSendingFacility( hdIn ); 181 } 182 183 /** 184 * Set the Sending facility VMDB Institution Id. 185 * @param sName String with VMDB facility name 186 * @param sId String with VMDB facility id 187 */ 188 public void setSendingFacility( String sName, String sId ) { 189 MSHSegment msh = getMSH(); 190 msh.setSendingFacility( sName, sId ); 191 } 192 193 /** 194 * Get the Sending facility VMDB Institution Id. 195 * @return String with VMDB facility id 196 */ 197 public String getSendingFacilityId() { 198 MSHSegment msh = getMSH(); 199 return msh.getSendingFacilityId(); 200 } 201 202 public HDElement getSendingFacility() { 203 MSHSegment msh = getMSH(); 204 return msh.getSendingFacility(); 205 } 206 207 /** 208 * Get the Sending facility name. 209 * @return String with VMDB facility name 210 */ 211 public String getSendingFacilityName() { 212 MSHSegment msh = getMSH(); 213 return msh.getSendingFacilityName(); 214 } 215 216 /** 217 * Set the Receiving facility HDElement 218 * @param hdIn HDElement with full facility 219 */ 220 public void setReceivingFacility( HDElement hdIn ) { 221 MSHSegment msh = getMSH(); 222 msh.setReceivingFacility( hdIn ); 223 } 224 225 /** 226 * Set the Receiving facility VMDB Institution Id. 227 * @param sName String with VMDB facility name 228 * @param sId String with VMDB facility id 229 */ 230 public void setReceivingFacility( String sName, String sId ) { 231 MSHSegment msh = getMSH(); 232 msh.setReceivingFacility( sName, sId ); 233 } 234 235 /** 236 * Get the Receiving facility HDElement. 237 * @return HDElement for receiving facility 238 */ 239 public HDElement getReceivingFacility() { 240 MSHSegment msh = getMSH(); 241 return msh.getReceivingFacility(); 242 } 243 244 /** 245 * Get the Receiving facility VMDB Institution Id. 246 * @return String with VMDB facility id 247 */ 248 public String getReceivingFacilityId() { 249 MSHSegment msh = getMSH(); 250 return msh.getReceivingFacilityId(); 251 } 252 253 /** 254 * Get the Receiving facility name. 255 * @return String with VMDB facility name 256 */ 257 public String getReceivingFacilityName() { 258 MSHSegment msh = getMSH(); 259 return msh.getReceivingFacilityName(); 260 } 261 262 /** 263 * Output the full rule string for this object.<br><br> 264 * This method differs from the inherited getRule() in that this method 265 * recurses into the rules for all contained loops so that we get the 266 * full rule for segment sequence.<br> 267 * This follows generally the HL7 convention of using [] for optionality 268 * and {} for repeatablility. Whitespace separates sequential non-repeating 269 * required elements. 270 * @return Rule as a string. 271 */ 272 public String printRule() { 273 RuleParser p = new RuleParser(); 274 return p.printRule( getRule() ); 275 } 276 277 /** 278 * Output the object as XML.<br><br> 279 * For HL7Message, this will be a full message. For all other objects it 280 * will be the DOM element for that object and all its contained objects. 281 * @return foratted XML text. 282 */ 283 public String toXML() { 284 String sXML = toXML(0); 285 XMLValidator val = new XMLValidator(); 286 if( !val.isValid( sXML ) ) { 287 System.err.println( "XML not valid by DTD" ); 288 } 289 return sXML; 290 } 291 292 /** 293 * Output the object as XML.<br><br> 294 * For HL7Message, this will be a full message. For all other objects it 295 * will be the DOM element for that object and all its contained objects. 296 * @param iDepth int value for the number of spaces to indent this object. Used just 297 * to make the XML easier to read as unformatted text. 298 * @return foratted XML text. 299 */ 300 public String toXML( int iDepth ) { 301 StringBuffer sb = new StringBuffer(); 302 sb.append( "<?xml version = \"1.0\" encoding = \"UTF-8\" standalone = \"no\"?>\n" ); 303 sb.append( "<!DOCTYPE " ); 304 MSHSegment msh = (MSHSegment)findSegment( "MSH" ); 305 if( msh == null ) return "ERROR: No MSH Header"; // Can't have a message without an MSH 306 String sMsgName = msh.getMessageName(); 307 sb.append( sMsgName ); 308 sb.append( " SYSTEM \"" ); 309 sb.append( sDTD ); 310 sb.append( "\">\n" ); 311 if( sStyleSheet != null ) { 312 sb.append( "<?xml:stylesheet type=\"text/xsl\" href=\"" ); 313 sb.append( sStyleSheet ); 314 sb.append( "\" ?>\n" ); 315 } 316 Iterator i = vSegments.iterator(); 317 for( int x = 0; x < iDepth; x++ ) sb.append( " " ); 318 sb.append( '<' ); 319 sb.append( sMsgName ); 320 sb.append( ">\n" ); 321 while( i.hasNext() ) { 322 HL7Object o = (HL7Object)i.next(); 323 sb.append( o.toXML( iDepth + 1 ) ); 324 } 325 for( int x = 0; x < iDepth; x++ ) sb.append( " " ); 326 sb.append( "</" ); 327 sb.append( sMsgName ); 328 sb.append( ">\n" ); 329 return sb.toString(); 330 } 331 332 }// End class HL7Message