/**
* Copyright (c) 2009-2010 Misys Open Source Solutions (MOSS) and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Contributors:
* Misys Open Source Solutions - initial API and implementation
* -
*/
package org.openhealthtools.common.utils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.SimpleTimeZone;
import org.openhealthexchange.openpixpdq.data.PatientIdentifier;
import org.openhealthexchange.openpixpdq.data.PersonIdentifier;
import org.openhealthexchange.openpixpdq.data.PersonName;
import org.openhealthexchange.openpixpdq.ihe.impl_v2.hl7.HL7v231;
import ca.uhn.hl7v2.parser.EncodingCharacters;
import ca.uhn.hl7v2.parser.Escape;
import com.misyshealthcare.connect.base.PatientID;
import com.misyshealthcare.connect.base.SharedEnums.SexType;
import com.misyshealthcare.connect.base.clinicaldata.Provider;
import com.misyshealthcare.connect.base.demographicdata.Address;
import com.misyshealthcare.connect.net.Identifier;
/**
* This class implements a number of HL7 parsing and generation utilities
* used by the XDS Repository Actors. The HAPI code could not be used in
* this class because the repository uses partial message pieces. It was easier
* just to implement them directly.
*
* @author Jim Firby
* @version 1.0 - Nov 14, 2005
*/
public class HL7 {
private static EncodingCharacters encodingCharacters = new EncodingCharacters('|', "^~\\&");
/**
* Generates a CX data type from an ID and an AssigningAuthority.
*
* @param id The ID
* @return The CX formatted type (conforming to XDS requirements)
*/
public static String toCX(String id, Identifier authority) {
if (id == null) return null;
StringBuffer sb = new StringBuffer();
addHL7Text(sb, id);
sb.append("^^^");
String value = authority.getNamespaceId();
if (value != null) sb.append(value);
sb.append("&");
value = authority.getUniversalId();
if (value != null) sb.append(value);
sb.append("&");
value = authority.getUniversalIdType();
if (value != null) sb.append(value);
return sb.toString();
}
/**
* Generates a CX list from a list of PatientIdentifier.
*
* @param pids The PatientIdentifier List
* @return The CX formatted type (conforming to XDS requirements)
*/
public static String toCX(List<PatientIdentifier> pids) {
if (pids == null || pids.size() == 0)
return null;
StringBuffer sb = new StringBuffer();
for (int i=0; i<pids.size(); i++) {
PersonIdentifier pid = pids.get(i);
String id = toCX(pid.getId(), pid.getAssigningAuthority());
sb.append(id);
if (i <= pids.size()-2) sb.append("~");
}
return sb.toString();
}
/**
* Generates a CX data type from an ID and an AssigningAuthority.
*
* @param id The ID
* @param authority The assigning authority
* @return The CX formatted type (conforming to XDS requirements)
*/
public static String toCX(String id, String authority) {
if (id == null || authority == null) return null;
StringBuffer sb = new StringBuffer();
addHL7Text(sb, id);
sb.append("^^^");
sb.append(authority);
return sb.toString();
}
/**
* Generates a CX data type from an AssigningAuthority.
*
* @param authority The assigning authority
* @return The CX formatted type
*/
public static String toCXAuth(Identifier authority) {
if(authority == null)
return null;
StringBuffer sb = new StringBuffer();
String value = authority.getNamespaceId();
if (value != null) sb.append(value);
value = authority.getUniversalId();
if (value != null) {
sb.append("&" + value);
value = authority.getUniversalIdType();
if (value != null) sb.append("&" + value);
}
return sb.toString();
}
/**
* Decodes an HL7 CX identifier string into a PatientID.
* <p>
* This method is used directly in some
* Mesa testing programs.
*
* @param pid The PatientID to hold the decoded patient ID
* @param cx The CX string holding the patient ID to decode
*/
public static void fromCX(PatientID pid, String cx) {
String id = getIdFromCX(cx);
Identifier authority = getAssigningAuthorityFromCX(cx);
if ((id == null) || (authority == null)) return;
authority.addPatientId(pid, id);
}
/**
* Gets the ID component from an HL7 CX identifier string.
*
* @param cx The CX string holding the ID to decode
*/
public static String getIdFromCX(String cx) {
return getFieldText(cx, 1);
}
/**
* Gets the Assigning Authority component from an HL7 CX identifier string.
*
* @param cx The CX string holding the Assigning Authority to decode
*/
public static Identifier getAssigningAuthorityFromCX(String cx) {
String aa = getFieldText(cx, 4);
if (aa == null) return null;
return new Identifier(getComponentText(aa, 1), getComponentText(aa, 2), getComponentText(aa, 3));
}
/**
* Gets the Assigning Authority component from an HL7 CX identifier Authority string.
*
* @param aa The CXAuth string holding the Assigning Authority to decode
*/
public static Identifier getAssigningAuthorityFromCXAuth(String aa) {
if (aa == null) return null;
return new Identifier(getComponentText(aa, 1), getComponentText(aa, 2), getComponentText(aa, 3));
}
/**
* Generates a DTM date/time type from a Java date object. The date
* and time is converted to GMT before formatting.
*
* @param date The Java date to format
* @return The DTM formatted date/time
*/
public static String toDTM(Date date) {
if (date == null) return null;
DateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
format.setTimeZone(new SimpleTimeZone(0, "GMT"));
return format.format(date);
}
/**
* Extracts a Java date object from a DTM date/time type. The date
* is converted from GMT to the local time zone.
*
* @param dtm The HL7 encoded DTM
* @return The Java date
*/
public static Date fromDTM(String dtm) {
if (dtm == null) return null;
return HL7v231.parseDateTimeGMT(dtm);
}
/**
* Generates a DT date type from a Java date object. The date
* is formatted in local time.
*
* @param date The Java date to format
* @return The DT formatted date
*/
public static String toDT(Date date) {
if (date == null) return null;
return HL7v231.formatDate(date);
}
/**
* Decodes a DT data type into a Java date object.
*
* @param date The DT date string
* @return The Java date object
*/
public static Date fromDT(String date) {
if (date == null) return null;
return HL7v231.parseDate(date);
}
/**
* Generates an XAD data type from an Address object.
*
* @param address The address to format
* @return The XAD formatted address string
*/
public static String toXAD(Address address) {
if (address == null) return null;
StringBuffer sb = new StringBuffer();
if (address.getAddLine1() != null) addHL7Text(sb, address.getAddLine1());
sb.append('^');
if (address.getAddLine2() != null) addHL7Text(sb, address.getAddLine2());
sb.append('^');
if (address.getAddCity() != null) addHL7Text(sb, address.getAddCity());
sb.append('^');
if (address.getAddState() != null) addHL7Text(sb, address.getAddState());
sb.append('^');
if (address.getAddZip() != null) addHL7Text(sb, address.getAddZip());
sb.append('^');
if (address.getAddCountry() != null) addHL7Text(sb, address.getAddCountry());
sb.append("^^^");
if (address.getAddCounty() != null) addHL7Text(sb, address.getAddCounty());
return sb.toString();
}
/**
* Decodes an HL7 XAD data type into an Address.
*
* @param xad The XAD encoded address
* @return The decoded address
*/
public static Address fromXAD(String xad) {
if (xad == null) return null;
Address address = new Address();
address.setAddLine1(getFieldText(xad, 1));
address.setAddLine2(getFieldText(xad, 2));
address.setAddCity(getFieldText(xad, 3));
address.setAddState(getFieldText(xad, 4));
address.setAddZip(getFieldText(xad, 5));
address.setAddCountry(getFieldText(xad, 6));
address.setAddCounty(getFieldText(xad, 9));
return address;
}
/**
* Generates an XON data type from a string.
*
* @param institution The string to be formatted
* @return The XON formatted string
*/
public static String toXON(String institution) {
return toHL7Text(institution);
}
/**
* Gets the institution string from an HL7 encoded XON.
*
* @param xon The XON to decode
* @return The institution
*/
public static String fromXON(String xon) {
return getFieldText(xon, 1);
}
/**
* Generates an XCN name data type from a Provider.
*
* @param person The person's name to be formatted
* @return The XCN formatted name string
*/
public static String toXCN(Provider person) {
if (person == null) return null;
StringBuffer sb = new StringBuffer();
addHL7Text(sb, person.getProviderId());
sb.append('^');
addHL7NameParts(sb, person.getProvNameFirst(), person.getProvNameMiddle(), person.getProvNameLast(),
person.getProvNameTitle(), person.getProvNameSuffix());
return sb.toString();
}
/**
* Extracts a Provide from an HL7 XCN data type.
*
* @param xcn The XCN to decode
* @return The resulting Provider
*/
public static Provider fromXCN(String xcn) {
if (xcn == null) return null;
Provider provider = new Provider();
provider.setProviderId(getFieldText(xcn, 1));
provider.setProvNameLast(getFieldText(xcn, 2));
provider.setProvNameFirst(getFieldText(xcn, 3));
provider.setProvNameMiddle(getFieldText(xcn, 4));
provider.setProvNameSuffix(getFieldText(xcn, 5));
provider.setProvNameTitle(getFieldText(xcn, 6));
return provider;
}
/**
* Generates an XPN name data type from a Patient.
*
* @param personName The patient's name to be formatted
* @return The XPN formatted name string
*/
public static String toXPN(PersonName personName) {
if (personName == null) return null;
StringBuffer sb = new StringBuffer();
addHL7NameParts(sb, personName.getFirstName(), personName.getSecondName(), personName.getLastName(),
personName.getPrefix(), personName.getSuffix());
return sb.toString();
}
/**
* Decodes an HL7 XPN data type and put the name fields into a <code>PersonName</code>
*
* @param personName The <code>PersonName</code> to get the name
* @param xpn The XPN data type to decode
* @return True if the name was decoded
*/
public static boolean fromXPN(PersonName personName, String xpn) {
if (personName == null) return false;
if (xpn == null) return false;
personName.setLastName(getFieldText(xpn, 1));
personName.setFirstName(getFieldText(xpn, 2));
personName.setSecondName(getFieldText(xpn, 3));
personName.setSuffix(getFieldText(xpn, 4));
personName.setPrefix(getFieldText(xpn, 5));
return true;
}
/**
* Converts an enum gender value to the coded value used in XDS
* HL7 PID-8 messages.
*
* @param sex The gender enum to convert
* @return The XDS HL7 value
*/
public static String toSex(SexType sex) {
if (sex == null) return "U";
if (sex == SexType.MALE) return "M";
if (sex == SexType.FEMALE) return "F";
if (sex == SexType.OTHER) return "O";
return "U";
}
/**
* Decodes and HL7 adminstrative sex value into a SexType enum.
*
* @param code The adminstrative sex code to decode
* @return The gender enum value
*/
public static SexType fromSex(String code) {
if (code == null) return SexType.UNKNOWN;
if (code.equalsIgnoreCase("M")) return SexType.MALE;
if (code.equalsIgnoreCase("F")) return SexType.FEMALE;
if (code.equalsIgnoreCase("O")) return SexType.OTHER;
return SexType.UNKNOWN;
}
/**
* Helper function to format a name
*
* @param sb The string buffer to add the name to
* @param f The first name
* @param m The middle name
* @param l The last name
* @param p The name prefix
* @param s The name suffix
*/
private static void addHL7NameParts(StringBuffer sb, String f, String m, String l, String p, String s) {
if (l != null) addHL7Text(sb, l);
if ((f == null) && (m == null) && (s == null) && (p == null)) return;
sb.append('^');
if (f != null) addHL7Text(sb, f);
if ((m == null) && (s == null) && (p == null)) return;
sb.append('^');
if (m != null) addHL7Text(sb, m);
if ((s == null) && (p == null)) return;
sb.append('^');
if (s != null) addHL7Text(sb, s);
if (p == null) return;
sb.append('^');
addHL7Text(sb, p);
}
/**
* Converts a string to acceptable HL7 by turning protected characters into
* spaces.
*
* @param sb The string buffer to put the result into
* @param input The input string to be converted
*/
private static void addHL7Text(StringBuffer sb, String input) {
if (input != null) {
sb.append(toHL7Text(input));
}
}
/**
* Encodes a string using HL7 escape characters.
*
* @param input The input string to encode
* @return The encoded HL7 string
*/
private static String toHL7Text(String input) {
if (input == null) return null;
return Escape.escape(input, encodingCharacters);
}
/**
* Decodes an HL7 string so that it contains no escape characters
*
* @param text The HL7 string to decode
* @return The decoded plain text string
*/
private static String fromHL7Text(String text) {
if (text == null) return null;
return Escape.unescape(text, encodingCharacters);
}
/**
* Gets the 'id' component from an HL7 CX string.
*
* @param input The CX string
* @return The 'id' component of the string
*/
public static String getCXid(String input) {
return fromHL7Text(getField(input, 1));
}
/**
* Gets the 'assigning authority' component from an HL7 CX string.
*
* @param input The CX string
* @return The 'assigning authority' component
*/
public static String getCXauthority(String input) {
String authority = getField(input, 4);
if (authority == null) return null;
if (authority.startsWith("&")) return authority.substring(1);
return authority;
}
/**
* Gets a field from an HL7 string
*
* @param input The input string
* @param field The field to extract
* @return
*/
private static String getField(String input, int field) {
return getPart(input, field, "\\^");
}
/**
* Gets the field text from an HL7 string.
*
* @param input The input string
* @param field The field to extract
* @return
*/
private static String getFieldText(String input, int field) {
String value = getField(input, field);
if (value == null) return null;
return fromHL7Text(value);
}
/**
* Gets a component from an HL7 field
*
* @param field The input field string
* @param component The component to extract
* @return
*/
private static String getComponent(String field, int component) {
return getPart(field, component, "\\&");
}
/**
* Gets a component text from an HL7 field.
*
* @param field The input field string
* @param component The component to extract
* @return
*/
private static String getComponentText(String input, int component) {
String value = getComponent(input, component);
if (value == null) return null;
return fromHL7Text(value);
}
/**
* Gets a piece of a delimited string.
*
* @param input The input string
* @param field The desired component (1 based)
* @param separator The component separator regex
* @return The value of the desired component
*/
private static String getPart(String input, int field, String separator) {
if (input == null) return null;
String fields[] = input.split(separator);
if (fields == null) return null;
if (fields.length < field) return null;
String result = fields[field - 1];
if (result == null) return null;
if (result.equals("")) return null;
return result;
}
}