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
- 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> ...
- 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> ...
- 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.
<bean id="sharedMappingService" class="org.openehealth.ipf.commons.map.BidiMappingService"> </bean>
<bean id="bidiMappingServiceConfigurer1" class="org.openehealth.ipf.commons.map.BidiMappingServiceConfigurer"> <property name="mappingService" ref="sharedMappingService" /> <property name="mappingScript" value="configurer1.map" /> </bean>
<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).
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:
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:
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:
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.