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