Jun 16, 2011

HL7 2.x DSL with auto-completion

Introduction 

Have you ever worked with a HL7 2.x [1]? Have you ever tried to extract information from a HL7 2.x message or even validate such a message against an IHE [2] profile with the HAPI library [3]? The purpose of the blog post is to announce that there is a Groovy [4] domain specific language (DSL) [5] that helps you work simpler with HL7 2.x messages. Most important, a Groovy DSL with auto-completion. To understand everything in this blog post, you need some basic knowledge about HL7 2.x and the HAPI library.

Why bother to create a HL7 2.x DSL with auto-completion?

HL7 is basically defined by a proprietary schema. The HAPI project has a legal permission to use this schema. What HAPI does is to generate Java classes, based on an abstract model for every version of HL7 2.x.
The need to extend HAPI has motivated the creation of such Groovy DSL, and why Groovy? Because in Groovy it is easy to create DSLs. However, there is something that has been somehow underestimated when it comes to DSLs - the IDE support. The IDE should be able help with both usage and verification of such DSL, and take away that burden from developers. Unfortunately not all IDEs support descriptors for Groovy, that are powerful enough to model such HL7 2.x DSL extensions. The good news: thanks to the Groovy Eclipse plugin version 2.5+ [6], the Eclipse IDE has this power [7].

How to get the HL7 2.x DSL?

The proposed HL7 2.x DSL is part of the Open eHealth IPF project [9]. If you use Maven 2, add the the HL7 DSL dependency in your pom.xml. Replace ${ipf-version} with 2.3-m2 or higher.

    org.openehealth.ipf.modules
    modules-hl7dsl
    ${ipf-version}

You will find the dependency in the Open eHealth Maven repository [8].
Next you have to add the dependencies with the generated HAPI structures you need. For example, if you work only with structures version 2.6, leave only the hapi-structures-v26 dependency.

    ca.uhn.hapi
    hapi-structures-v25
    1.0.1



    ca.uhn.hapi
    hapi-structures-v251
    1.0.1



    ca.uhn.hapi
    hapi-structures-v26
    1.0.1



What are the benefits of the HL7 2.x DSL? 

Before I show how to use the DSL,  I would like to list briefly its benefits:

  •  Groups and Segments can be accessed by name like an object property. This brings readability and HL7-like syntax. For example message.MSH will access the MSH segment in a message. 
  • Fields are accessed like an array field; components in a composite field are accessed like a multi-dimension array. For example: message.MSH[9][1] will return the message type
  • The internal structure changes between HL7 versions. In higher versions, primitive fields are sometimes replaced with composite fields, having the so far used primitive as first component. Using the DSL you can access the fileds by name. If the field or group is non-repeatable, the first element is returned. For example, message.PATIENT_RESULT(0).PATIENT will equal message.PATIENT_RESULT.PATIENT
  • Support for isEmpty() method for all adapted HAPI elements (ca.uhn.hl7v2.model.Segment, ca.uhn.hl7v2.model.Group and all kind of ca.uhn.hl7v2.model.Type, inclusive ca.uhn.hl7v2.model.Composite types).
  • Support for getPath() method; Here is a quick example. For a ORU_R01 message:  msg.PATIENT_RESULT(1).PATIENT.PID.path will return "PATIENT_RESULT(1).PATIENT.PID" (1 means the second repetition of PATIENT_RESULT, works only with the access by name)
  • Adding repettions, for example new MessageAdapter(new ORU_R01()).PATIENT_RESULT(0) will add a PATIENT_RESULT group to the empty message.
  • Manipulating segments and fields, for example msg1.EVN.from(msg2.EVN) copies the EVN segment from msg2 to msg1
How does the auto-completion work?

Well, first you have to type in a Eclipse Groovy editor. The Groovy Eclipse plug-in's type-inferencing engine, with the help of a DSLD descriptor provided by the DSL library, does most of it. The DSLD descriptor publishes callbacks that are invoked when the Groovy abstract syntax tree is being modified. However, the implementation needs a hint - your adapter declaration must be parametrized with generics. That's it, the rest is done by the DSLD descriptor of the DSL library and the plug-in's inferencing engine. Imagine that you adapt a version 2.4 ORU_R01 message. Your adapter definition should be parametrized with generics like this:

import static org.openehealth.ipf.modules.hl7dsl.MessageAdapters.load
import org.openehealth.ipf.modules.hl7dsl.MessageAdapter
import ca.uhn.hl7v2.model.v24.message.ORU_R01
...

    MessageAdapter <ORU_R01> msg = load('msg-oru-r01.hl7');
...
Notice the generic argument ORU_R01. If you do not use the generic arg, you will not profit from the auto-completion.

Examples

Let's say you have declared the msg2 object as in the previous section:
MessageAdapter <ORU_R01> msg2 = load('msg-oru-r01.hl7'); When you hit ctrl + space after msg2. you will get the suggestions. The following two screen-shots show the access by name in action.
Auto-completion after access by name of the second repetition of PATIENT_RESULT group
DSL Documenation. Notice the currentType (SegmentAdapter in bold)
You also have suggestions for the adapter HAPI objects. It is valid to call the regular HAPI methods on those objects. You can use the suggested methods, however in this case the getPath() method will return an empty String object. As said before getPath() currently works only with access by name.
Notice the HAPI documentation on the left. Notice the getAt(Integer) method  - provided by the DSL as well.


What is currently not working?
  • If you use the DSL for getAt(Integer)(a.k.a. the [] syntax) on a SegmentAdapter you get the wrong proposals for SegmentAdapter after the getAt(Integer). So do not use the auto-completion after the [] DSL. The returned type is TypeAdapter in this case. Example: if you hit ctrl + space after msg.MSH[1]. you will get the proposals for the SegmentAdapter. This will be fixed in the next versions of the Groovy Eclipse Plugin - there is already a task for it.
  • Sometimes the auto-completion does not return the SegmentAdapter type. It returns Object instead. I hope that this will be fixed soon.

Conclusion
The Open eHealth IPF project has a HL7 2.x DSL module. The module comes with a DSLD descriptor that provides type inference information in Eclipse. The DSL in a combination with the auto-completion helps to navigate and access the HL7 elements easily. Many thanks to the Open eHealth IPF project team and the people who developed the Groovy Eclipse plugin.

References
[1] HL7 http://en.wikipedia.org/wiki/Health_Level_7
[2] IHE http://www.ihe.net/
[3] HAPI library http://hl7api.sourceforge.net/
[4] Groovy programming language http://groovy.codehaus.org/
[5] Domain Specific Language (DSL) http://www.martinfowler.com/bliki/DomainSpecificLanguage.html
[6] Groovy Eclipse plugin http://groovy.codehaus.org/Eclipse+Plugin
[7] Better DSL support in Groovy-Eclipse http://blog.springsource.com/2011/05/08/better-dsl-support-in-groovy-eclipse/
[8] Open eHealth Maven repository http://repo.openehealth.org/maven2/releases/
[9] Open eHealth IPF project Wiki http://repo.openehealth.org/confluence/display/ipf2/IPF+reference+-+multi

No comments:

Post a Comment