org.regenstrief.hl7.util.UtilHL7.java Source code

Java tutorial

Introduction

Here is the source code for org.regenstrief.hl7.util.UtilHL7.java

Source

/**
 * The contents of this file are subject to the Regenstrief Public License
 * Version 1.0 (the "License"); you may not use this file except in compliance with the License.
 * Please contact Regenstrief Institute if you would like to obtain a copy of the license.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) Regenstrief Institute.  All Rights Reserved.
 */
package org.regenstrief.hl7.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.regenstrief.hl7.HL7Data;
import org.regenstrief.hl7.HL7Parser;
import org.regenstrief.hl7.HL7Properties;
import org.regenstrief.hl7.MessageProperties;
import org.regenstrief.hl7.convert.OutputUtil;
import org.regenstrief.hl7.datatype.CE;
import org.regenstrief.hl7.datatype.CWE;
import org.regenstrief.hl7.datatype.NM;
import org.regenstrief.hl7.group.UMSG_Z01;
import org.regenstrief.hl7.segment.PID;
import org.regenstrief.util.Util;
import org.regenstrief.util.criterion.AntiCriterion;
import org.regenstrief.util.criterion.CharSequenceContainsCriterion;
import org.regenstrief.util.reflect.ReflectUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.regex.Pattern;

/**
 * <p>
 * Title: Utility (HL7)
 * </p>
 * <p>
 * Description: HL7 Utility Class
 * </p>
 * <p>
 * Copyright: Copyright (c) 2005
 * </p>
 * <p>
 * Company: Regenstrief Institute
 * </p>
 * 
 * @author Andrew Martin
 * @version 1.0
 */
public class UtilHL7 {

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

    public final static String NS_URI_V2 = "urn:hl7-org:v2xml";

    public final static String NS_URI_V3 = "urn:hl7-org:v3";

    // Primitive data types
    public final static String FT_XML = "FT";

    public final static String NM_XML = "NM";

    public final static String ST_XML = "ST";

    public final static String TX_XML = "TX";

    public final static String TM_XML = "TM";

    // Pseudo-segment, appended to previous segment in message
    public final static String ADD_XML = "ADD";

    public final static String PACKAGE = "org.regenstrief.hl7.";

    public final static String DATATYPE = PACKAGE + "datatype.";

    public final static String SEGMENT = PACKAGE + "segment.";

    public final static String GROUP = PACKAGE + "group.";

    public final static String PACKAGE_EXT = PACKAGE + "ext.";

    //public final static String DATATYPE_EXT = PACKAGE_EXT + "datatype.";
    public final static String SEGMENT_EXT = PACKAGE_EXT + "segment.";

    public final static String GROUP_EXT = PACKAGE_EXT + "group.";

    public final static Package PACKAGE_GROUP = UMSG_Z01.class.getPackage();

    private final static int NULL_NUMERIC = -1;

    private final static Map<String, Constructor<? extends HL7Data>> nameCache = new HashMap<String, Constructor<? extends HL7Data>>();

    private final static Map<Class<? extends HL7Data>, Constructor<? extends HL7Data>> classCache = new HashMap<Class<? extends HL7Data>, Constructor<? extends HL7Data>>();

    private final static Pattern PAT_BR = Pattern.compile("\\n|\\r\\n");

    static {
        final AntiCriterion tc = AntiCriterion.getInstance(new CharSequenceContainsCriterion("Test", false));
        final Class<?> hierarchy[] = new Class<?>[] { UMSG_Z01.class, PID.class, CE.class };
        final HL7Parser parser = new HL7Parser();
        final String versions[] = new String[] { OutputUtil.V2_3_1, OutputUtil.V2_4 };
        final MessageProperties mp = parser.getMessageProp();

        for (final Class<?> hc : hierarchy) {
            final Class<?>[] a = Util.getClassesForPackage(hc, tc);
            for (final String version : versions) { // Groups have different names in different versions
                mp.setVersion(version);
                for (final Class<?> ac : a) {
                    final Class<? extends HL7Data> c = Util.cast(ac);
                    if (Modifier.isAbstract(c.getModifiers())) {
                        continue;
                    }
                    final HL7Data data = getInstance(parser, c);
                    if (data != null) { // These packages also contain test classes which can be ignored
                        final Constructor<? extends HL7Data> cons = ReflectUtil.getConstructor(c,
                                HL7Properties.class);
                        nameCache.put(data.getTagName(), cons);
                        classCache.put(cons.getDeclaringClass(), cons);
                    }
                }
            }
        }
        if (!nameCache.containsKey(PID.PID_XML)) {
            final StringBuilder b = new StringBuilder();
            for (final String n : nameCache.keySet()) {
                if (b.length() > 0) {
                    b.append(", ");
                }
                b.append(n);
            }
            log.error("Failed to load PID class; available HL7 types/segments/groups:");
            log.error(b);
        }
    }

    public final static String getNamespaceURI() {
        return NS_URI_V2;
    }

    public final static String getQualifiedName(final String localName) {
        return localName;
    }

    /**
     * Converts a String into a List
     * 
     * @param s the String
     * @return the List
     **/
    public final static List<String> stringToStringList(final String s) {
        return Util.length(s) == 0 ? null : Util.splitExactIntoList(s, '\n');
    }

    /**
     * Converts a String into a List
     * 
     * @param prop the HL7Properties
     * @param s the String
     * @return the List
     **/
    public final static List<CE> stringToCEList(final HL7Properties prop, final String s) {
        List<CE> list = null;
        String[] tokens = null;
        int i = 0;

        if ((s == null) || (s.length() == 0)) {
            return null;
        }

        tokens = Util.splitExact(s, '\n');
        list = new ArrayList<CE>(tokens.length);

        for (i = 0; i < tokens.length; i++) {
            list.add(new CE(prop, tokens[i]));
        }

        return list;
    }

    /**
     * Converts a String into an List
     * 
     * @param prop the HL7Properties
     * @param s the String
     * @return the List
     **/
    public final static List<CWE> stringToCWEList(final HL7Properties prop, final String s) {
        List<CWE> list = null;
        String[] tokens = null;
        int i = 0;

        if ((s == null) || (s.length() == 0)) {
            return null;
        }

        tokens = Util.splitExact(s, '\n');
        list = new ArrayList<CWE>(tokens.length);

        for (i = 0; i < tokens.length; i++) {
            list.add(CWE.createCWE(prop, tokens[i]));
        }

        return list;
    }

    // Might be a good place to use a ConverterIterable
    /**
     * Converts the given List of CEs to a List of CWEs
     * 
     * @param inList the List of CEs
     * @return the List of CWEs
     **/
    public final static List<CWE> CEListToCWEList(final List<CE> inList) {
        List<CWE> outList = null;
        int i = 0;
        final int size = Util.size(inList);

        if (size == 0) {
            return null;
        }

        outList = new ArrayList<CWE>(size);
        for (i = 0; i < size; i++) {
            outList.add(inList.get(i).toCWE());
        }

        return outList;
    }

    /**
     * Converts the given List of CWEs to a List of CWs
     * 
     * @param inList the List of CWEs
     * @return the List of CEs
     **/
    public final static List<CE> CWEListToCEList(final List<CWE> inList) {
        List<CE> outList = null;
        int i = 0;
        final int size = Util.size(inList);

        if (size == 0) {
            return null;
        }

        outList = new ArrayList<CE>(size);
        for (i = 0; i < size; i++) {
            outList.add(inList.get(i).getCE());
        }

        return outList;
    }

    /**
     * Retrieves the double value of a Double
     * 
     * @param d the Double
     * @return the double
     **/
    public final static double getDouble(final Number d) {
        return d == null ? NULL_NUMERIC : d.doubleValue();
    }

    /**
     * Retrieves the double value of an NM
     * 
     * @param nm the NM
     * @return the double
     **/
    public final static double getDouble(final NumericData nm) {
        return getDouble(AbstractNumericData.toDouble(nm)); // nm == null ? NULL_NUMERIC : nm.doubleValue(); // nm.doubleValue() can throw NullPointerException
    }

    /**
     * Retrieves the int value of a Number
     * 
     * @param d the Number
     * @return the int
     **/
    public final static int getInt(final Number d) {
        return d == null ? NULL_NUMERIC : d.intValue();
    }

    /**
     * Retrieves the Integer value of a Double
     * 
     * @param d the Double
     * @return the Integer
     **/
    public final static Integer getInteger(final Number d) {
        return d == null ? null : new Integer(d.intValue());
    }

    /**
     * Retrieves the Integer value of a NumericData
     * 
     * @param nd the NumericData
     * @return the Integer
     **/
    public final static Integer getInteger(final NumericData nd) {
        return nd == null ? null : getInteger(nd.toDouble());
    }

    /**
     * Wraps a double in a Double
     * 
     * @param d the double
     * @return the Double
     **/
    public final static Double getDouble(final double d) {
        return d < 0 ? null : new Double(d);
    }

    /**
     * Wraps a double in an NM
     * 
     * @param prop the HL7Properties
     * @param d the double
     * @return the NM
     **/
    public final static NM getNM(final HL7Properties prop, final double d) {
        return d < 0 ? null : new NM(prop, d);
    }

    /**
     * Retrieves the Double value of an Integer
     * 
     * @param i the Integer
     * @return the Double
     **/
    public final static Double getDouble(final Integer i) {
        return i == null ? null : new Double(i.intValue());
    }

    /**
     * Wraps an Integer in an NM
     * 
     * @param prop the HL7Properties
     * @param i the Integer
     * @return the NM
     **/
    public final static NM getNM(final HL7Properties prop, final Integer i) {
        return i == null ? null : new NM(prop, i.doubleValue());
    }

    /**
     * Parses the String into an int
     * 
     * @param s the String
     * @return the int
     **/
    public final static int parseInt(final String s) {
        return s == null ? NULL_NUMERIC : Integer.parseInt(s);
    }

    /**
     * Concatenates two Objects
     * 
     * @param o1 the first Object
     * @param o2 the second Object
     * @return the concatenated String
     **/
    public final static String concat(final Object o1, final Object o2) {
        return concat(o1, o2, null);
    }

    /**
     * Concatenates two Objects
     * 
     * @param o1 the first Object
     * @param o2 the second Object
     * @param delim the delimiter to put between the Objects
     * @return the concatenated String
     **/
    public final static String concat(final Object o1, final Object o2, final String delim) {
        return Util.concatWithDelim(toString(o1), toString(o2), Util.unNull(delim));
    }

    /**
     * Converts an Object to a display String
     * 
     * @param o the Object
     * @return the String
     **/
    public final static String toString(final Object o) {
        return o instanceof Double ? Util.doubleToString((Double) o)
                : o instanceof HL7Data ? ((HL7Data) o).toDisplay() : Util.toString(o);
    }

    /**
     * Converts an Object to a piped String
     * 
     * @param o the Object
     * @return the String
     **/
    public final static String toPiped(final Object o) {
        return o instanceof HL7Data ? ((HL7Data) o).toPiped() : toString(o);
    }

    /**
     * Converts a List to a piped String
     * 
     * @param list the List
     * @return the String
     **/
    public final static String toPiped(final List<?> list) {
        if (Util.isEmpty(list)) {
            return null;
        }

        final StringBuilder sb = new StringBuilder();
        for (final Object o : list) {
            Util.appendSeparator(sb, '~').append(toPiped(o));
        }

        return sb.toString();
    }

    /**
     * Retrieves the HL7 version
     * 
     * @param prop the HL7Properties
     * @return the HL7 version
     **/
    public final static String getVersion(final HL7Properties prop) {
        if (prop == null) {
            return null;
        }
        final MessageProperties mp = prop.getMessageProp();
        return mp == null ? null : mp.getVersion();
    }

    /**
     * Retrieves whether the version is 2.4 or greater
     * 
     * @param prop the HL7Properties
     * @return whether the version is 2.4 or greater
     **/
    public final static boolean isVersion24Compatible(final HL7Properties prop) {
        return isVersion2xCompatible(prop, '4');
    }

    private final static boolean isVersion2xCompatible(final HL7Properties prop, final char x) {
        //return getVersion(prop).compareTo(OutputUtil.V2_4) >= 0;
        final String v = getVersion(prop);
        return v == null ? false : v.charAt(2) >= x; // Works for values like 2.1, 2.2, 2.3.1, 2.4, 2.5, 2.6
    }

    public final static boolean isVersion27Compatible(final HL7Properties prop) {
        return isVersion2xCompatible(prop, '7');
    }

    /**
     * Retrieves a Class of the desired type
     * 
     * @param valueType the desired type
     * @return the Class
     **/
    public final static Class<? extends HL7Data> getClass(final String valueType) {
        final Constructor<? extends HL7Data> cons = nameCache.get(valueType);
        return cons == null ? null : cons.getDeclaringClass();
    }

    /**
     * Retrieves the names of known types/segments/etc.
     * 
     * @return the names
     **/
    public final static Set<String> getTypes() {
        return nameCache.keySet();
    }

    /**
     * Retrieves an instance Object of the desired class
     * 
     * @param prop the HL7Properties
     * @param c the desired class
     * @param <T> the desired type
     * @return the instance Object
     **/
    @SuppressWarnings("unchecked")
    public final static <T extends HL7Data> T getInstance(final HL7Properties prop, final Class<T> c) {
        if (c == null) {
            return null;
        }
        try {
            final Constructor<?> cache = classCache.get(c);
            final Constructor<T> cons = cache == null ? ReflectUtil.getConstructor(c, HL7Properties.class)
                    : (Constructor<T>) cache;
            return cons == null ? null : cons.newInstance(prop);
        } catch (final Exception e) {
            throw new RuntimeException("Could not instantiate " + c.getName(), e);
        }
    }

    /**
     * Retrieves an instance Object of the desired class
     * 
     * @param prop the HL7Properties
     * @param valueType the desired class
     * @return the instance Object
     **/
    public final static HL7Data getInstance(final HL7Properties prop, final String valueType) {
        final Constructor<? extends HL7Data> cons = nameCache.get(valueType);
        final HL7Data data;
        try {
            data = cons == null ? null : cons.newInstance(prop);
        } catch (final Exception e) {
            throw new RuntimeException("Could not instantiate " + valueType, e);
        }
        return data == null ? null : data.getTagName().equals(valueType) ? data : null;
    }

    /**
     * Determines if a String contains valid NM data: A number represented as a series of ASCII
     * numeric characters consisting of an optional leading sign (+ or -), the digits and an
     * optional decimal point. In the absence of a sign, the number is assumed to be positive. If
     * there is no decimal point the number is assumed to be an integer.
     * 
     * @param s the String
     * @return whether the String contains valid NM data
     **/
    public final static boolean isNumeric(final String s) {
        return indexOfNonNumeric(s) == -1;
    }

    /**
     * Retrieves first index of a character in the given String that violates the NM definition: A
     * number represented as a series of ASCII numeric characters consisting of an optional leading
     * sign (+ or -), the digits and an optional decimal point. In the absence of a sign, the number
     * is assumed to be positive. If there is no decimal point the number is assumed to be an
     * integer.
     * 
     * @param s the String
     * @return whether the String contains valid NM data
     **/
    public final static int indexOfNonNumeric(final String s) {
        int i = 0;
        final int len = Util.length(s);
        boolean hasDecimal = false, hasDigit = false;

        if (len == 0) {
            return -1;
        }

        char c = s.charAt(0);
        if ((c == '+') || (c == '-')) {
            i++;
        }

        for (; i < len; i++) {
            c = s.charAt(i);
            if (Util.isDigit(c)) {
                hasDigit = true;
                continue;
            }
            if (c == '.') {
                if (hasDecimal) {
                    return i;
                }
                hasDecimal = true;
                continue;
            }
            return i;
        }

        return hasDigit ? -1 : len - 1;
    }

    public final static String getNumeric(final String s) {
        if (Util.isEmpty(s)) {
            return null;
        }
        final int i = indexOfNonNumeric(s);
        return i == -1 ? s : s.substring(0, i);
    }

    public final static boolean isTimestamp(final String s) {
        final int size = Util.length(s);
        //        8  1214
        // YYYYMMDDHHMMSS
        if ((size != 8) && (size != 12) && (size != 14)) {
            return false;
        }
        if (!Util.isAllDigits(s)) {
            return false;
        }
        final int year = Util.parseInt(s, 0, 4);
        if ((year < 1980) || (year > 2040)) {
            return false;
        }
        final int month = Util.parseInt(s, 4, 6);
        if ((month < 1) || (month > 12)) {
            return false;
        }
        final int day = Util.parseInt(s, 6, 8);
        if ((day < 1) || (day > 31)) {
            return false;
        }
        if (size == 8) {
            return true;
        }
        final int hour = Util.parseInt(s, 8, 10);
        if ((hour < 0) || (hour > 24)) {
            return false;
        }
        final int minute = Util.parseInt(s, 10, 12);
        if ((minute < 0) || (minute > 59)) {
            return false;
        }
        if (size == 12) {
            return true;
        }
        final int second = Util.parseInt(s, 12, 14);
        if ((second < 0) || (second > 59)) {
            return false;
        }
        return true;
    }

    /**
     * Clears the HL7Data
     * 
     * @param data the HL7Data
     **/
    public final static void clear(final HL7Data data) {
        if (data != null) {
            data.clear();
        }
    }

    /**
     * Adds the tokens of the given String to the given List
     * 
     * @param list the List
     * @param s the String
     * @return the List
     **/
    public final static List<String> addTokens(List<String> list, final String s) {
        final String[] tokens = Util.splitExact(s, '\n', -1);
        final int size = Util.length(tokens);

        if (size == 0) {
            /*
            This should be right for NTE parsing; empty repetitions should be added to the list.
            It's probably right for anything else invoking this, if there is anything.
            */
            list = Util.add(list, "");
        } else {
            for (int i = 0; i < size; i++) {
                list = Util.addIfNotNull(list, tokens[i]);
            }
        }

        return list;
    }

    /**
     * Adds the tokens of the given String to the given List
     * 
     * @param list the List
     * @param s the String
     * @return the List
     **/
    public final static List<Object> addTokensToObjectList(List<Object> list, final String s) {
        //final String[] tokens = Util.splitExact(s, '\n', -1); // If s has \r\n breaks, this keeps \r in the Strings
        final String[] tokens = PAT_BR.split(s, -1);

        for (int i = 0; i < tokens.length; i++) {
            list = Util.addIfNotNull(list, tokens[i]);
        }

        return list;
    }

    public final static HL7Parser getParser(final HL7Properties prop) {
        return prop instanceof HL7Parser ? (HL7Parser) prop : null;
    }
}