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