Note: Content Must be Reviewed for Accuracy and moved to Wiki format.

Home/ How To/ To do/ Definitions/ Content Handlers/ Data Types/ Contact

How To:

Use of the message API requires an implementation of the RIM and Data Types classes discussed in objective six. Whether an application developer uses the reference implementation provided by the Java SIG or writes his own is optional. Key to the processes of parsing and building, is access to an HMD. There is an HMD for each message type. HMDs are tabular in nature and can be kept in an RDBMS or in an XML document. HMDs map the node name of a message element to a RIM type. Because this is a Java implementation the RIM type is a java interface and not a class. At present, the JavaSIG uses a properties file as a map between the interface name and the name of its implementing class.

Parsing

Parsing of a message requires (1) a message, (2) a corresponding HMD, and (3) an ICM. Given a message, the processor parses the message using implementations of

org.xml.sax.ContentHandler.

These implementations implement the callback methods that are common to SAX processing 1. As parsing begins, the names of the message type and its  HMD  are extracted from the message. These values are used to access the HMD and to extract from it the appropriate metadata for the given message type.

As parsing continues, elements and their attributes are encountered in the message stream.  Upon these encounters, the processor accesses the HMD to retrieve the interface name that corresponds to the element or attribute being processed. With the interface name, the ICM is accessed to retrieve the name of its implementing class. 2. With the class name, the processor calls the corresponding factory method, which returns an object of the class's type. Each time such an object is returned it is passed into a content handler which takes over the processing of the message stream with which it populates the object. There are content handlers for the message, for the elements within a message, and for each data type.

  • org.hl7.xml.MessageContentHandler
  • org.hl7.xml.MessageElementContentHandler
  • org.sax.xml.ContentHandler

Implementing Content Handlers.

A content handler implementation is needed for each data type. These should be derived fromSimpleTypeContentHandler. They must override these methods:

  • getResult()
  • notifyActivation(Attributes atts)

In addition, all content handlers that expect nested content should implement the method notifyResult(Object result)

notifyActivation(Attributes atts) is needed to inform the current content handler of attributes the belong to the current element. returnResults(Object result) is called to restore processing to the content handler that was in effect before the current content handler was activated.

The following snippets show how these methods were implemented in CSContentHandler.


  protected void notifyActivation(Attributes atts) {
     String nullFlavorString = atts.getValue("nullFlavor");
     if(nullFlavorString != null) {
       this._result = CSnull.valueOf(nullFlavorString);
     } else {
       String valueString = atts.getValue("code");
       if(valueString != null) {
         this._result = CSimpl.valueOf(valueString,"unknown");
       }
     }
   }
 


  protected Object getResult() {
     if(this._result == null)
       return CSnull.NI;
     else 
      return this._result;
   }

DynamicContentHandlerBase (a super class of SimpleTypeContentHandler) simplifies the activation of a new DynamicContentHandler by providing the suspendWith() method. suspendWith() deals with all the content handler changes and the passing of results.

CAVEAT: Make extra sure that all your handlers that expect nested results EXPLICITLY IMPLEMENT the ResultReceiver interface. The DynamicContentHandlerBase does NOT implement the ResultReceiver interface by itself.

Building

Building

Building a message requires (1) a populated object graph and, (2) an HMD that corresponds to the message type being built. It follows a reverse of the parsing.

From the v3Ballot:

"...the HMD completely specifies an exact, serialized traversal of the class instances (clones) and the class attributes that participate in a message type..."

Given a populated object graph and the message type, the builder uses the HMD in the manner described above to retrieve the name of the XML element or attribute that is written into the message.

This means that a DOM is built with elements and attributes named as dictated by the HMD and with their values set with data pulled from the object graph.

The build process has not yet been designed.

Data Types

This how to is limited to those who are implementing data types that are to be contributed to the Java SIG codebase. The design of the message API is such that you can supply your own implementations of both the RIM classes and data types and do so at runtime. List of Data Types
  • Data Type interfaces follow the inheritance tree as defined in the HL7 specification.
  • Data Type implementations always extend ANYImpl.
  • Data Types are immutable and trier constructors are private. ANYImpl.

You will notice that each interface is implemented by two classes; one class has a suffix of "impl" and the other has a suffix of null. Implementations with the suffix "impl", which of course, stands for implementation are the real working classes. Implementations with the suffix null are used when the data type is to be null (as opposed to simple setting the java null).  In such an implementation, all methods return one of the NullFlavors.

Data types are implemented with a public static method called valueOf(... , which is the way a data type is instantiated.

CS cs = CSImpl.valueOf("codeString","codeSystemString");

The following snippets are from CSimpl.java and CSnull.java respectively. They show the two possible impementations of valueOf(... for the CS data type.

  public static CS valueOf(String codeString,
			   String codeSystemString) {
    ST code = STjlStringAdapter.valueOf(codeString);
    UID codeSystem = UIDimpl.valueOf(codeSystemString);
    if(code.isNullJ() || codeSystem.isNullJ()) {
      return CSnull.NI;  // enforce that we need a root!
      // FIXME: should this sort of convenience be one level up?
      // I mean, we could just throw an IllegalArgumentException?
    } else {
      CSimpl theCS = new CSimpl(code, codeSystem);

      return theCS;
    }
  }

//////////////////

  public static CSnull valueOf(String nullFlavorString) {
    /** We first intern the argument because we'll compare it with 
	constants that are always interned.
    */
    String nf = nullFlavorString.intern();
    if(nf == NullFlavor.sNI) return NI;
    else if(nf == NullFlavor.sNA) return NA;
    else if(nf == NullFlavor.sUNK) return UNK;
    else if(nf == NullFlavor.sNASK) return NASK;
    else if(nf == NullFlavor.sASKU) return ASKU;
    else if(nf == NullFlavor.sNAV) return NAV;
    else if(nf == NullFlavor.sOTH) return OTH;
    else throw new IllegalArgumentException("null flavor " + nf);
  }

Using the ICM (Interface Class Mapping)

The Java SIG has created a reference implementation of the RIM and its supporting data types. You, however, may want to supply your own implementations--ones that meet your particular needs. The design of the message processing engine accommodates this through the use of interfaces. There is an interface for each RIM class and for each data type. It is these interface names that are contained in the HMD and which the HMD cross references to the tag names in the message elements. When the message engine, while parsing a message, encounters an element; the name of that element, its tag name, is taken to the HMD and used as a key value in looking up the name of the corresponding interface. When a content handler is in effect, it takes the interfaces name and used if as a key in looking up the name of the implmenting class. Example:
HMD ICM
Tag Interface Interface Class
ObservationEvent Observation Observation net.gungho.med.Observation
At the present, the ICM is implemented as a simple properties file. Example: Observation=net.gungho.med.Observation

1 i.e. startDocument, stopDocument, startElement, stopElement, and characters

2 A developer can cause the message engine to use his implementation of the interfaces by creating a mapping that points to his implementing classes