org.openmrs.hl7.HL7Util.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.hl7.HL7Util.java

Source

/**
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
 *
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
 * graphic logo is a trademark of OpenMRS Inc.
 */
package org.openmrs.hl7;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.api.APIException;
import org.openmrs.api.context.Context;
import org.openmrs.util.OpenmrsConstants;
import org.openmrs.util.OpenmrsUtil;

import ca.uhn.hl7v2.HL7Exception;

/**
 * HL7-related utilities
 *
 * @version 1.0
 */
public class HL7Util {

    private static Log log = LogFactory.getLog(HL7Util.class);

    // Date and time format parsers
    private static final String TIMESTAMP_FORMAT = "yyyyMMddHHmmss.SSSZ";

    private static final String TIME_FORMAT = "HHmmss.SSSZ";

    public static final String LOCAL_TIMEZONE_OFFSET = new SimpleDateFormat("Z").format(new Date());

    /**
     * Converts an HL7 timestamp into a java.util.Date object. HL7 timestamps can be created with
     * varying levels of precision — e.g., just the year or just the year and month, etc.
     * Since java.util.Date cannot store a partial value, we fill in defaults like January, 01 at
     * midnight within the current timezone.
     *
     * @param s HL7 timestamp to be parsed
     * @return Date object
     * @throws HL7Exception
     * @should fail on 78
     * @should handle 1978
     * @should fail on 19784
     * @should handle 197804
     * @should fail on 197841
     * @should handle 19780411
     * @should fail on 197804116
     * @should handle 1978041106
     * @should fail on 19780411065
     * @should handle 197804110615
     * @should fail on 1978041106153
     * @should handle 19780411061538
     * @should handle 19780411061538.1
     * @should handle 19780411061538.12
     * @should handle 19780411061538.123
     * @should handle 19780411061538.1234
     * @should fail on 197804110615-5
     * @should handle 197804110615-05
     * @should handle 197804110615-0200
     * @should not flub dst with 20091225123000
     */
    public static Date parseHL7Timestamp(String s) throws HL7Exception {

        // HL7 dates must at least contain year and cannot exceed 24 bytes
        if (s == null || s.length() < 4 || s.length() > 24) {
            throw new HL7Exception("Invalid date '" + s + "'");
        }

        StringBuffer dateString = new StringBuffer();
        dateString.append(s.substring(0, 4)); // year
        if (s.length() >= 6) {
            dateString.append(s.substring(4, 6)); // month
        } else {
            dateString.append("01");
        }
        if (s.length() >= 8) {
            dateString.append(s.substring(6, 8)); //day
        } else {
            dateString.append("01");
        }

        // Parse timezone (optional in HL7 format)
        String timeZoneOffset;
        try {
            Date parsedDay = new SimpleDateFormat("yyyyMMdd").parse(s.substring(0, 8));
            timeZoneOffset = getTimeZoneOffset(s, parsedDay);
        } catch (ParseException e) {
            throw new HL7Exception(
                    "Error parsing date: '" + s.substring(0, 8) + "' for time zone offset'" + s + "'", e);
        }
        s = s.replace(timeZoneOffset, ""); // remove the timezone from the string

        if (s.length() >= 10) {
            dateString.append(s.substring(8, 10)); // hour
        } else {
            dateString.append("00");
        }
        if (s.length() >= 12) {
            dateString.append(s.substring(10, 12)); // minute
        } else {
            dateString.append("00");
        }
        if (s.length() >= 14) {
            dateString.append(s.substring(12, 14)); // seconds
        } else {
            dateString.append("00");
        }
        if (s.length() >= 15 && s.charAt(14) != '.') {
            // decimal point
            throw new HL7Exception("Invalid date format '" + s + "'");
        } else {
            dateString.append(".");
        }
        if (s.length() >= 16) {
            dateString.append(s.substring(15, 16)); // tenths
        } else {
            dateString.append("0");
        }
        if (s.length() >= 17) {
            dateString.append(s.substring(16, 17)); // hundredths
        } else {
            dateString.append("0");
        }
        if (s.length() >= 18) {
            dateString.append(s.subSequence(17, 18)); // milliseconds
        } else {
            dateString.append("0");
        }

        dateString.append(timeZoneOffset);

        Date date;
        try {
            date = new SimpleDateFormat(TIMESTAMP_FORMAT).parse(dateString.toString());
        } catch (ParseException e) {
            throw new HL7Exception("Error parsing date '" + s + "'");
        }
        return date;
    }

    /**
     * Gets the timezone string for this given fullString. If fullString contains a + or - sign, the
     * strings after those are considered to be the timezone. <br>
     * <br>
     * If the fullString does not contain a timezone, the timezone is determined from the server's
     * timezone on the "givenDate". (givenDate is needed to account for daylight savings time.)
     *
     * @param fullString the hl7 string being parsed
     * @param givenDate the date that should be used if no timezone exists on the fullString
     * @return a string like +0500 or -0500 for the timezone
     * @should return timezone string if exists in given string
     * @should return timezone for givenDate and not the current date
     */
    protected static String getTimeZoneOffset(String fullString, Date givenDate) {
        // Parse timezone (optional in HL7 format)
        String timeZoneOffset;
        int tzPlus = fullString.indexOf('+');
        int tzMinus = fullString.indexOf('-');
        boolean timeZoneFlag = (tzPlus > 0 || tzMinus > 0);
        if (timeZoneFlag) {
            int tzIndex;
            if (tzPlus > 0) {
                tzIndex = tzPlus;
            } else {
                tzIndex = tzMinus;
            }
            timeZoneOffset = fullString.substring(tzIndex);
            if (timeZoneOffset.length() != 5) {
                log.error("Invalid timestamp because its too short: " + timeZoneOffset);
            }

        } else {
            //set default timezone offset from the current day
            Calendar cal = Calendar.getInstance();
            cal.setTime(givenDate);
            timeZoneOffset = new SimpleDateFormat("Z").format(cal.getTime());
        }

        return timeZoneOffset;
    }

    /**
     * Convenience method for parsing HL7 dates (treated just like a timestamp with only year,
     * month, and day specified)
     *
     * @see org.openmrs.hl7.HL7Util#parseHL7Timestamp(String)
     * @throws HL7Exception
     */
    public static Date parseHL7Date(String s) throws HL7Exception {
        return parseHL7Timestamp(s);
    }

    /**
     * Converts an HL7 time into a java.util.Date object. Since the java.util.Date object cannot
     * store just the time, the date will remain at the epoch (e.g., January 1, 1970). Time more
     * precise than microseconds is ignored.
     *
     * @param s HL7 time to be converted
     * @return Date object set to time specified by HL7
     * @throws HL7Exception
     * @should fail on 197804110615
     * @should handle 0615
     * @should handle 061538
     * @should handle 061538.1
     * @should handle 061538.12
     * @should handle 061538.123
     * @should handle 061538.1234
     * @should handle 061538-0300
     */
    public static Date parseHL7Time(String s) throws HL7Exception {

        String timeZoneOffset = getTimeZoneOffset(s, new Date());
        s = s.replace(timeZoneOffset, ""); // remove the timezone from the string

        StringBuffer timeString = new StringBuffer();

        if (s.length() < 2 || s.length() > 16) {
            throw new HL7Exception("Invalid time format '" + s + "'");
        }

        timeString.append(s.substring(0, 2)); // hour
        if (s.length() >= 4) {
            timeString.append(s.substring(2, 4)); // minute
        } else {
            timeString.append("00");
        }
        if (s.length() >= 6) {
            timeString.append(s.substring(4, 6)); // seconds
        } else {
            timeString.append("00");
        }
        if (s.length() >= 7 && s.charAt(6) != '.') {
            // decimal point
            throw new HL7Exception("Invalid time format '" + s + "'");
        } else {
            timeString.append(".");
        }
        if (s.length() >= 8) {
            timeString.append(s.substring(7, 8)); // tenths
        } else {
            timeString.append("0");
        }
        if (s.length() >= 9) {
            timeString.append(s.substring(8, 9)); // hundredths
        } else {
            timeString.append("0");
        }
        if (s.length() >= 10) {
            timeString.append(s.subSequence(9, 10)); // milliseconds
        } else {
            timeString.append("0");
        }

        // Parse timezone (optional in HL7 format)
        timeString.append(timeZoneOffset);

        Date date;
        try {
            date = new SimpleDateFormat(TIME_FORMAT).parse(timeString.toString());
        } catch (ParseException e) {
            throw new HL7Exception("Invalid time format: '" + s + "' [" + timeString + "]", e);
        }
        return date;
    }

    /**
     * Gets the destination directory for hl7 archives.
     *
     * @return The destination directory for the hl7 in archive
     */
    public static File getHl7ArchivesDirectory() throws APIException {
        String archiveDir = Context.getAdministrationService()
                .getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_HL7_ARCHIVE_DIRECTORY);

        if (StringUtils.isBlank(archiveDir)) {
            log.warn("Invalid value for global property '" + OpenmrsConstants.GLOBAL_PROPERTY_HL7_ARCHIVE_DIRECTORY
                    + "', trying to set a default one");
            archiveDir = HL7Constants.HL7_ARCHIVE_DIRECTORY_NAME;

            log.debug("Using '" + archiveDir
                    + "' in the application data directory as the root directory for hl7_in_archives");
        }

        //TODO Should take care of the case where the user is using removable media, this might explode
        return OpenmrsUtil.getDirectoryInApplicationDataDirectory(archiveDir);
    }
}