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