Mapping Service

Mapping Service

Overview

The org.openehealth.ipf.commons.map.MappingService interface deals with the requirement that message processing often involves mapping between code systems, i.e. from one set of codes into a corresponding set of codes. For example, HL7 version 2 to HL7 version 3 use different code systems for most coded values like message type, gender, clinical encounter type, marital status codes, address and telecommunication use codes, just to mention a few. MappingService implementations provide the mapping logic, which can be a simple java.util.Map, but can also be a facade for a remote terminology service.
The commons-map component extends the java.lang.String and java.util.Collection classes with methods targeted at mapping.

The commons-map library provides one MappingService implementation (org.openehealth.ipf.commons.map.BidiMappingService), which implements

  • bidirectional mapping
  • mapping of arbitrary objects
  • definitions of mappings using external Groovy Scripts

Configuring the Mapping Service

This section explains how to configure IPF's BidiMappingService.

Using the BidiMappingService bean

  1. Add the necessary dependencies to your project's Maven 2 descriptor.
    pom.xml
    ...
    <!-- Dependency for accessing and manipulating HL7 v2 structures -->
    <dependency>
        <groupId>org.openehealth.ipf.commons</groupId>
        <artifactId>commons-map</artifactId>
        <version>${ipf-version}</version>
    </dependency>
    ...
    


  2. To use BidiMappingService, define a Spring bean and initialize it with the external Groovy resource:
    context.xml
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:lang="http://www.springframework.org/schema/lang"
           xmlns:camel="http://camel.apache.org/schema/spring"
           xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/lang 
    http://www.springframework.org/schema/lang/spring-lang-2.5.xsd
    http://camel.apache.org/schema/spring
    http://camel.apache.org/schema/spring/camel-spring.xsd">
    
    ...
    <!-- Groovy class that provides the operations on the mappings -->
    <bean id="myMappingService" class="org.openehealth.ipf.commons.map.BidiMappingService">
       <property name="mappingScript" value="classpath:example.groovy"/>
    </bean>
    ...
    
  3. Register the Mapping Service extensions in the Spring Application Context of your application
    context.xml
    
    ...
    
        <!-- Mapping extensions -->
        <bean id="mappingExtension" 
            class="org.openehealth.ipf.commons.map.extend.MappingExtension">
            <property name="mappingService" ref="myMappingService" />
        </bean>
    
        <!-- Register the extensions -->
        <bean id="routeModelExtender"
            class="org.openehealth.ipf.platform.camel.core.extend.DefaultModelExtender">
            <property name="routeModelExtensions">
                <list>
                    ...
                    <ref bean="mappingExtension" />
                </list>
            </property>
        </bean>
    
        ...
    
    </beans>
    

Using the BidiMappingServiceConfigurer bean

The BidiMappingServiceConfigurer supports distributed configurations of a BidiMappingService instance. This is useful when several components want to contribute mappings to a shared mapping service. Each component defines a BidiMappingServiceConfigurer bean in its application context that references the shared mapping service.

context-main.xml
<bean id="sharedMappingService" 
    class="org.openehealth.ipf.commons.map.BidiMappingService">
</bean>
context-component1.xml
<bean id="bidiMappingServiceConfigurer1" 
    class="org.openehealth.ipf.commons.map.BidiMappingServiceConfigurer">
    <property name="mappingService" ref="sharedMappingService" />
    <property name="mappingScript" value="configurer1.map" />
</bean>
context-component2.xml
<bean id="bidiMappingServiceConfigurer2" 
    class="org.openehealth.ipf.commons.map.BidiMappingServiceConfigurer">
    <property name="mappingService" ref="sharedMappingService" />
    <property name="mappingScripts">
        <list>
            <value>configurer2.map</value>
            <value>configurer3.map</value>
        </list>
    </property>
</bean>

From IPF version 2.3.0 the BidiMappingServiceConfigurer is marked as deprecated. The application developers are encouraged to use the new IPF extension mechanism instead.

Definition of Mappings

A mapping example is displayed below. The example maps a couple of codes from HL7-related code systems (see HL7 Messaging for more details on IPF's HL7 support).

example.groovy
mappings = {
   encounterType(['2.16.840.1.113883.12.4','2.16.840.1.113883.5.4'],
      E : 'EMER',
      I : 'IMP',
      O : 'AMB'
)

   vip(['2.16.840.1.113883.12.99','2.16.840.1.113883.5.1075'],
      Y : 'VIP',
      (ELSE) : { it }
)

   messageType(
      'ADT^A01' : 'PRPA_IN402001'
     (ELSE) : { throw new HL7Exception("Invalid message type", 207) }
   )
}

This defines three mappings (encounterType, vip, and messageType), having an optional definition for ISO Object Identifiers (OIDs) to identify key and value code systems. The encounterType mapping has three entries, while the vip and messageType mappings have only one.

You can use the mappingService directly, or you can take advantage of the MappingExtension:

example.groovy
   def x = mappingService.get('encounterType', 'E') // using the service bean reference
   def y = 'E'.map('encounterType')   // more concise: using the dynamic map method
   def z = 'E'.mapEncounterType()     // even more concise
   // x == y == z == 'EMER'

The ELSE entry is called on MappingService.get() request with unknown keys. ELSE can be

  • a Closure, which takes the key as parameter and is then executed
  • any other Object o, which will return o.toString().

In the example above,

  • for the vip mapping the key is returned, so that mappingService.get('vip', 'X') == 'X'
  • for the messageType mapping, an Exception is thrown.

The services also allow mapping in the backward direction:

example.groovy
   def x = mappingService.getKey('vip', 'VIP')  // Y
   def y = 'VIP'.mapReverse('vip')              // Y
   def z = 'VIP'.mapReverseVip()                // Y
Ambiguous mappings

In case that a mapping definition maps more than one key to the same value (e.g. A->C and B->C), the backward mapping only contains the last entry, i.e. C->B.

BidiMappingService also can be initialized using a list of mapping files:

...
<!-- Groovy class that provides the operations on the mappings -->
<bean id="myMappingService" class="org.openehealth.ipf.commons.map.BidiMappingService">
  <property name="mappingScripts">
    <list>
      <value>classpath:example1.groovy</value>
      <value>classpath:example2.groovy</value>
    </list>
  </property>
</bean>
...

Conflicting mappings are overridden by later list entries, i.e. mappings defined in example2.groovy override existing mappings defined in example1.groovy.

Furthermore, BidiMappingService supports default reverse mappings, i.e. you can specify an ELSE mapping also from the reverse direction:

example2.groovy
mappings = {
  reverseMapping(
    key            : 'value',
    (ELSE)         : 'unknownKey',
    'unknownValue' : (ELSE)
  )

  reverseMappingWithClosures(
    key       : 'value',
    (ELSE)    : 'unknownKey',
    { 'key' } : (ELSE)
  )
}

The reverseMappingWithClosures mapping also demonstrates how to use a closure in order to return a default key that is already defined as key for a regular mapping.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.