001    package org.vmdb.hl7;
002    
003    import java.util.*;
004    
005    /**
006     * <p><Title:> Abstract Base Class for HL7 Data Type Elements. </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>HL7 uses data elements in a nested structure of fields sometimes composed of
011     * components sometimes composed of subcomponents.  The Generic HL7Element class
012     * forms the base of all our data type elements.  It adds to knowledge of its
013     * own level (field, component, or subcomponent) and the machinery to build
014     * itself by parsing HL7 input under the direction of a RuleParser.</p>
015     * @author Michael K. Martin
016     * @version 1.0
017     */
018    
019    public abstract class HL7Element extends HL7Object {
020       static final int FIELD = 1;
021       static final int COMPONENT = 2;
022       static final int SUBCOMPONENT = 3;
023       protected Vector vComponents;
024       protected String sNextComp;
025       protected int iMaxComps;
026       protected int iCompNo;
027       protected StringList slComps;
028       protected String sType;
029       protected int iLevel;
030       // What I really wanted to do was to allow derived classes to override these
031       // values.  Instead at this point each one must call setName and setRule so
032       // these variables are sort of a waste.
033       private String sNm = "ZZ";
034       private String sRl = "*";
035    
036       /**
037        * Construct an Element using the name and type specified in its sNm and sRl
038        * constants.
039        * @param iLevel One of the constants for specifying level as FIELD, COMPONENT, or
040        * SUBCOMPONENT.  Default FIELD.
041        */
042       public HL7Element( int iLevel ) {
043          this.iLevel = iLevel;
044          setName( sNm );
045          setRule( sRl );
046       }
047    
048       /**
049        * Construct an Element using the name and type specified in its sNm and sRl
050        * constants.  And at the level of a Field.
051        */
052       public HL7Element() {
053          this.iLevel = FIELD;
054          setName( sNm );
055          setRule( sRl );
056          //initialize();
057       }
058    
059       /**
060        * Construct an Element of given name and type.<br><br>
061        * @param sName The name of this element relative to its container.  Example:
062        * "PID.1" for the segquenceID in the pid segment.
063        * @param sType The HL7 data type abbreviation.  Example "ST" for string or
064        * "NM" for numeric.  Note: Both of these would be instatiated as SimpleElement
065        * objects in this library.
066        */
067       public HL7Element( String sName, String sType ) {
068          this.iLevel = FIELD;
069          setName( sName );
070          setType( sType );
071          setRule( sRl );
072          //initialize();
073       }
074    
075       /**
076        * Return this element to its empty state.  Either emptying its collection
077        * of components or setting its value to an empty string.
078        */
079       public void clear() {
080          if( vComponents != null )
081             vComponents.clear();
082          else
083             setValue( "" );
084       }
085    
086       /**
087        * Factory method to make an element of an appropriate subclass
088        * for the type specified.<br><br>
089        * It will either be a specific subclass
090        * of type TYPE + Element or a SimpleElement if the specified type
091        * is either any of the single string types or not yet defined.
092        * @param iLevel the FIELD, COMPONENT, or SUBCOMPONENT constant
093        * to define the level for this element
094        * @param sSeparators the separators for this message
095        * @param sType the type name (CE, XAD, ST, etc. for this Element)
096        * @param sName the name of the new element such as PID.3
097        */
098       public static HL7Element makeElement( int iLevel,
099                                             String sSeparators,
100                                             String sType,
101                                             String sName ) {
102          HL7Element e = null;
103          try {
104             String sClass = "org.vmdb.hl7." + sType + "Element";
105             Class c = Class.forName( sClass );
106             e = (HL7Element)c.newInstance();
107             e.setLevel( iLevel );
108          }
109          catch( Exception ex ) {
110             e = new SimpleElement( iLevel );
111             e.setType( sType );
112          }
113          e.setSeparators( sSeparators );
114          e.setName( sName );
115          return e;
116       }
117    
118       /**
119        * Set this Element to a specified level.<br><br>
120        * @param iLevel One of the constants for specifying level as FIELD, COMPONENT, or
121        * SUBCOMPONENT.  Default FIELD.
122        */
123       public void setLevel( int iLevel ) { this.iLevel = iLevel; }
124    
125       /**
126        * Get this Elements level.
127        * @return One of the constants for specifying level as FIELD, COMPONENT, or
128        * SUBCOMPONENT.  Default FIELD.
129        */
130       public int getLevel() { return this.iLevel; }
131    
132       /**
133        * Is this Element empty?
134        * @return true if this element has no components (and no value in the
135        * special derived case of SimpleElement).
136        */
137       public boolean empty() {
138          if( (vComponents == null || vComponents.size() == 0) )
139             return true;
140          else
141             return false;
142       }
143    
144       /**
145        * How many components are in this element?<br><br>
146        * Includes all components up to the last required or populated one.
147        * @return integer number of component objects present.
148        */
149       public int size() {
150          if( empty() ) return 0;
151          int iCount = 0;
152          Iterator i = vComponents.iterator();
153          while( i.hasNext() ) {
154             HL7Element e = (HL7Element)i.next();
155             iCount += e.size();
156          }
157          return iCount;
158       }
159    
160       /**
161        * Populate the first component with the value supplied.<br><br>
162        * Many complex element types are commonly populated in only the first
163        * component.  This method allows them to be set as if they were a simple
164        * element.
165        * @param sValue String value to set.
166        */
167       public void setValue( String sValue ) {
168          if( vComponents == null ) initialize();
169          HL7Element e = getComponent( 1 );
170          if( e == null ) {
171             addEmptyComponents( 1 );
172             e = getComponent( 1 );
173          }
174          e.setValue( sValue );
175       }
176    
177       /**
178        * Get the first component value.<br><br>
179        * Many complex element types are commonly populated in only the first
180        * component.  This method allows them to be accessed as if they were a simple
181        * element.
182        * @return String value from first component.
183        */
184       public String getValue()
185       {
186          if( empty() )
187             return "";
188          else
189             return getComponent( 1 ).getValue();
190       }
191    
192       /**
193        * Shallow copy of source to new class set original to null!
194        */
195    /*   void copyFrom( HL7Element element )
196       {
197          vComponents = element.vComponents;
198          sType = element.sType;
199          sName = element.sName;
200          iLevel = element.iLevel;
201       }
202    */
203       void addComponent( HL7Element element ) {
204          if( vComponents == null ) {
205             initialize();
206          }
207          vComponents.add( element );
208       }
209    
210       /**
211        * Set component based on one based index position.
212        * @param element HL7Element derived object to set at location
213        * @param iLoc location in element to set (one based index).
214        * @throws ArrayIndexOutOfBoundsException if iLoc points to a field
215        * less than one or greater than the number of components allowed
216        * in the message rule.
217        * @throws MalformedFieldException if the HL7Element provided as element does not
218        * have the type required by rule for this component.
219        */
220       public void setComponent( HL7Element element, int iLoc )
221          throws ArrayIndexOutOfBoundsException,
222                 MalformedFieldException
223       {
224    
225          if( vComponents == null ) {
226             initialize();
227          }
228          if( iLoc < 1 || iLoc > iMaxComps )
229             throw new ArrayIndexOutOfBoundsException();
230          else if( vComponents.size() < iMaxComps ) {
231             addEmptyComponents( iLoc );
232          }
233          String sType = getComponent( iLoc ).getType();
234          if( !sType.equals("*") && !sType.equalsIgnoreCase( element.getType() ) ) {
235             throw new MalformedFieldException( "Found component type " + element.getType() +
236                                                " but expected " + sType  );
237          }
238          vComponents.setElementAt( element, iLoc - 1 );
239       }
240    
241       /**
242        * Get component based on one based index position.
243        * @param iLoc location in element to get (one based index).
244        * @return Reference to the HL7Element derived object at the specified location
245        * or null if the location does not yet exist.
246        * @throws ArrayIndexOutOfBoundsException if iLoc points to a field
247        * less than one or greater than the number of components allowed
248        * in the message rule.
249        */
250       public HL7Element getComponent( int iLoc ) {
251          if( vComponents == null ) {
252             initialize();
253          }
254          if( iLoc < 1 || iLoc > iMaxComps )
255             throw new ArrayIndexOutOfBoundsException( "iLoc = " + iLoc + " iMaxComps = " + iMaxComps );
256          if( vComponents != null && iLoc <= vComponents.size() && iLoc > 0 )
257             return (HL7Element)vComponents.elementAt( iLoc - 1 );
258          else
259             return null;
260       }
261    
262       /**
263        * Get an Iterator over all the components in this element.
264        * @return Iterator over zero or more components.
265        */
266       public Iterator iterator() {
267          if( vComponents == null ) {
268             initialize();
269          }
270          return vComponents.iterator();
271       }
272    
273       /**
274        * Set the HL7 data type of this element.<br><br>
275        * @param sType The HL7 data type abbreviation.  Example "ST" for string or
276        * "NM" for numeric.  Note: Both of these would be instatiated as SimpleElement
277        * objects in this library.
278        */
279       public void setType( String sType ) {
280          this.sType = sType;
281          if( vComponents != null ) {
282             Iterator i = vComponents.iterator();
283             int iElementNo = 1;
284             while( i.hasNext() ) {
285                HL7Element e = (HL7Element)i.next();
286                e.setType( sType );
287                e.setName( sType + "." + iElementNo++ );
288             }
289          }
290       }
291    
292    
293       /**
294        * Get the HL7 data type of this element.<br><br>
295        * @return The HL7 data type abbreviation.  Example "ST" for string or
296        * "NM" for numeric.  Note: Both of these would be instatiated as SimpleElement
297        * objects in this library.
298        */
299       public String getType() { return sType; }
300    
301       boolean readString( String sElement ) {
302          if( sElement == null || sElement.length() == 0 ) {
303             return true;
304          }
305          // need to read tokens themselves so we can account for empty fields
306          char cEsc = getSeparator( ESC_SEP );
307          char cThisSep = iLevel == FIELD ? getSeparator( COMP_SEP ) : getSeparator( SUB_SEP );
308          slComps = new StringList( sElement, cThisSep );
309          // OK, we have more than a simple value, so need to intialize the Vector
310          initialize();
311          iCompNo = 1;
312          if( slComps.hasMoreStrings() ) {
313             sNextComp = slComps.nextString();
314             // Pass this pointer so parser knows to call my processSegment method.
315             RuleParser p = new RuleParser();
316             try {
317                if( p.parseComponents( getRule(), this ) > 0 )
318                   return true;
319             }
320             catch( Exception e ) {
321                System.err.println( e.toString() );
322                e.printStackTrace();
323             }
324          }
325          return false;
326       }
327    
328       public void initialize() {
329          RuleParser p = new RuleParser();
330          vComponents = new Vector();
331          try {
332             iMaxComps = p.initializeComponents( getRule(), this );
333          }
334          catch( Exception e ) {
335             System.err.println( e.toString() );
336             e.printStackTrace();
337          }
338       }
339    
340       boolean processElement( String sField, String sType )
341              throws org.vmdb.hl7.MalformedFieldException {
342          boolean bRetVal = false;
343          if( sType == null || sType.length() == 0 )
344             bRetVal = false;
345          else if( sNextComp == null ) {
346             if( slComps.hasMoreStrings() )
347                sNextComp = slComps.nextString();
348             else
349                sNextComp = null;
350             bRetVal = true;
351          }
352          else {
353             // Check field against definition of sField
354             HL7Element e = null;
355             e = HL7Element.makeElement( this.getLevel() + 1, getSeparators(), sType, sField );
356             e.readString( sNextComp );
357             setComponent( e, iCompNo++ );
358             bRetVal = true;
359             if( slComps.hasMoreStrings() )
360                sNextComp = slComps.nextString();
361             else
362                sNextComp = null;
363          }
364          return bRetVal;
365       }// End processElement
366    
367       protected void addEmptyComponents( int iCompNo ) {
368          RuleParser p = new RuleParser();
369          if( vComponents == null )
370             initialize();
371          try {
372             p.addComponents( getRule(), this, vComponents.size(), iCompNo-1 );
373          }
374          catch( Exception e ) {
375             System.err.println( e.toString() );
376             e.printStackTrace();
377          }
378       }
379    
380       /**
381        * Return the element as an HL7String.<br><br>
382        * This should probably be renamed toHL7() but for now the only logical
383        * string representation is the raw HL7.
384        * @return HL7 string of this element.
385        */
386       public String toString() {
387          StringBuffer sb = new StringBuffer();
388          if( vComponents == null )
389             return "";
390          Iterator i = vComponents.iterator();
391          char cSep = ' ';
392          if( iLevel == FIELD ) cSep = getSeparator( COMP_SEP );
393          if( iLevel == COMPONENT ) cSep = getSeparator( SUB_SEP );
394          while( i.hasNext() ) {
395             HL7Element eNext = (HL7Element)i.next();
396             if( eNext != null )
397                sb.append( eNext.toString() );
398             if( i.hasNext() ) sb.append( cSep );
399          }
400          return sb.toString();
401       }
402    
403       /**
404        * Return the element as an HL7String.<br><br>
405        * This should probably be renamed toHL7() but for now the only logical
406        * string representation is the raw HL7.
407        * @return HL7 string of this element.
408        */
409       public String toHL7String() {
410          StringBuffer sb = new StringBuffer();
411          if( vComponents == null )
412             return "";
413          Iterator i = vComponents.iterator();
414          char cSep = ' ';
415          if( iLevel == FIELD ) cSep = getSeparator( COMP_SEP );
416          if( iLevel == COMPONENT ) cSep = getSeparator( SUB_SEP );
417          while( i.hasNext() ) {
418             HL7Element eNext = (HL7Element)i.next();
419             if( eNext != null )
420                sb.append( eNext.toHL7String() );
421             if( i.hasNext() ) sb.append( cSep );
422          }
423          return sb.toString();
424       }
425    
426       /**
427        * Output the element as XML.<br><br>
428        * Outputs the DOM element for this element and all its contained objects.
429        * @param iDepth int value for the number of spaces to indent this object.  Used just
430        * to make the XML easier to read as unformatted text.
431        * @return foratted XML text.
432        */
433       public String toXML ( int iDepth ) {
434          if( size() == 0 ) {
435             return "";
436          }
437          StringBuffer sb = new StringBuffer();
438          if( this instanceof SimpleElement ) {
439             for( int x = 0; x < iDepth; x++ ) sb.append( " " );
440             sb.append( '<' );
441             sb.append( getName() );
442             sb.append( ">" );
443             sb.append( getValue() );
444             sb.append( "</" );
445             sb.append( getName() );
446             sb.append( ">\n" );
447          }
448          else {
449             Iterator i = vComponents.iterator();
450             int iFieldNo = 0;
451             for( int x = 0; x < iDepth; x++ ) sb.append( " " );
452             sb.append( '<' );
453             sb.append( getName() );
454             sb.append( ">\n" );
455             while( i.hasNext() ) {
456                HL7Element eNext = (HL7Element)i.next();
457                sb.append( eNext.toXML( iDepth + 1 ) );
458             }
459             for( int x = 0; x < iDepth; x++ ) sb.append( " " );
460             sb.append( "</" );
461             sb.append( getName() );
462             sb.append( ">\n" );
463          }
464          return sb.toString();
465       }
466    
467       boolean elementComplete() {
468          return( !slComps.hasMoreStrings() );
469       }
470    
471       void addComponent( String sElementName, String sElementType ) {
472          HL7Element e;
473          e = HL7Element.makeElement( this.getLevel() + 1, getSeparators(), sElementType, sElementName );
474          vComponents.add( e );
475       }
476    
477    }// End class HL7Element
478