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