com.phdcc.homehealth.Hapi.java Source code

Java tutorial

Introduction

Here is the source code for com.phdcc.homehealth.Hapi.java

Source

package com.phdcc.homehealth;

import java.io.IOException;
import org.json.JSONObject;
import org.json.XML;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.llp.MinLowerLayerProtocol;
import ca.uhn.hl7v2.model.GenericMessage;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.parser.CanonicalModelClassFactory;
import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
import ca.uhn.hl7v2.parser.GenericModelClassFactory;
import ca.uhn.hl7v2.parser.PipeParser;
import ca.uhn.hl7v2.util.Terser;
import ca.uhn.hl7v2.validation.impl.DefaultValidation;
import ca.uhn.hl7v2.model.v25.message.ADT_A01;
import ca.uhn.hl7v2.model.v25.message.ADT_AXX;
import ca.uhn.hl7v2.model.v25.message.ORU_R01;

public class Hapi {
    /**
     * The contents of this file are subject to the Mozilla Public License Version 1.1
     * (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.mozilla.org/MPL/
     * 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.
     *
     * The Original Code is "ExampleUseTerser.java".  Description:
    * "Example Code"
    *
    * The Initial Developer of the Original Code is University Health Network. Copyright (C)
    * 2001.  All Rights Reserved.
    *
    * Contributor(s): James Agnew
    *
    * Alternatively, the contents of this file may be used under the terms of the
    * GNU General Public License (the  GPL), in which case the provisions of the GPL are
    * applicable instead of those above.  If you wish to allow use of your version of this
    * file only under the terms of the GPL and not to allow others to use your version
    * of this file under the MPL, indicate your decision by deleting  the provisions above
    * and replace  them with the notice and other provisions required by the GPL License.
    * If you do not delete the provisions above, a recipient may use your version of
    * this file under either the MPL or the GPL.
    *
    */

    /**
     * Example code illustrating how to handle multiple versions of HL7 from one codebase
     * 
     * @author <a href="mailto:jamesagnew@sourceforge.net">James Agnew</a>
     * @version $Revision: 1.1 $ updated on $Date: 2011-05-22 16:52:21 $ by $Author: jamesagnew $
     */
    public class HandlingMultipleVersions {

        //public static void main(String[] args) throws Exception {
        public void HapiParse(String hl7message) throws Exception {

            /*
             * Often, you will need to handle multiple versions of HL7 from a sending system
             * from within the same code base. Because HAPI uses different model classes for
             * each version, this can seem difficult.
             * 
             * Before the first example, a bit of background information that is useful.
             * HL7 v2 is a backwards compatible standard, for the most part. New versions
             * of the standard will deprocate old fields and segments and groups, but they never
             * remove them entirely. They will also rename fields and groups, but this has
             * no effect on encoded messages if they are encoded using ER7 (pipe and hat)
             * encoding, only on the message structure objects themselves.
             * 
             * Unfortunately, because of this renaming, it is not possible for the
             * HAPI library to create a single version of a structure JAR which covers
             * all versions of HL7 v2 (v2.1, v2.2, v2.3, etc). That said, it is always
             * possible to use a HAPI message structure object to parse or encode a
             * message of the same type from an earlier version of the standard. In
             * other words, if you have a v2.2 ADT^A01 message, you can use the v2.3
             * ADT_A01 structure class to parse it, and you can also use the v2.3 ADT_A01
             * structure class to create a new v2.2 message if you are not planning on
             * XML encoding it.  
             * 
             * The following example shows two ways of dealing with this situation. First,
             * for this example, consider the following messages. Each is identical, aside
             * from the version string: "2.5" and "2.3".
             */

            String v25message = "MSH|^~\\&|ULTRA|TML|OLIS|OLIS|200905011130||ORU^R01|20169838-v25|T|2.5\r"
                    + "PID|||7005728^^^TML^MR||TEST^RACHEL^DIAMOND||19310313|F|||200 ANYWHERE ST^^TORONTO^ON^M6G 2T9||(416)888-8888||||||1014071185^KR\r"
                    + "PV1|1||OLIS||||OLIST^BLAKE^DONALD^THOR^^^^^921379^^^^OLIST\r"
                    + "ORC|RE||T09-100442-RET-0^^OLIS_Site_ID^ISO|||||||||OLIST^BLAKE^DONALD^THOR^^^^L^921379\r"
                    + "OBR|0||T09-100442-RET-0^^OLIS_Site_ID^ISO|RET^RETICULOCYTE COUNT^HL79901 literal|||200905011106|||||||200905011106||OLIST^BLAKE^DONALD^THOR^^^^L^921379||7870279|7870279|T09-100442|MOHLTC|200905011130||B7|F||1^^^200905011106^^R\r"
                    + "OBX|1|ST|||Test Value";

            String v23message = "MSH|^~\\&|ULTRA|TML|OLIS|OLIS|200905011130||ORU^R01|20169838-v23|T|2.3\r"
                    + "PID|||7005728^^^TML^MR||TEST^RACHEL^DIAMOND||19310313|F|||200 ANYWHERE ST^^TORONTO^ON^M6G 2T9||(416)888-8888||||||1014071185^KR\r"
                    + "PV1|1||OLIS||||OLIST^BLAKE^DONALD^THOR^^^^^921379^^^^OLIST\r"
                    + "ORC|RE||T09-100442-RET-0^^OLIS_Site_ID^ISO|||||||||OLIST^BLAKE^DONALD^THOR^^^^L^921379\r"
                    + "OBR|0||T09-100442-RET-0^^OLIS_Site_ID^ISO|RET^RETICULOCYTE COUNT^HL79901 literal|||200905011106|||||||200905011106||OLIST^BLAKE^DONALD^THOR^^^^L^921379||7870279|7870279|T09-100442|MOHLTC|200905011130||B7|F||1^^^200905011106^^R\r"
                    + "OBX|1|ST|||Test Value";

            /*
             * The first (and probably better in most ways) technique is as follows. Use a model class
             * factory called the CanonicalModelClassFactory. This class forces a specific version of
             * HL7 to be used. Because HL7 v2.x is a backwards compatible standard, you can choose the
             * highest version you need to support, and the model classes will be compatible with
             * messages from previous versions.
             */

            HapiContext context = new DefaultHapiContext();

            // Create the MCF. We want all parsed messages to be for HL7 version 2.5,
            // despite what MSH-12 says.
            CanonicalModelClassFactory mcf = new CanonicalModelClassFactory("2.5");
            context.setModelClassFactory(mcf);

            // Pass the MCF to the parser in its constructor
            PipeParser parser = context.getPipeParser();

            // The parser parses the v2.3 message to a "v25" structure
            ORU_R01 msg = (ORU_R01) parser.parse(v23message);

            // 20169838-v23
            System.out.println(msg.getMSH().getMessageControlID().getValue());

            // The parser also parses the v2.5 message to a "v25" structure
            msg = (ORU_R01) parser.parse(v25message);

            // 20169838-v25
            System.out.println(msg.getMSH().getMessageControlID().getValue());

            /*
             * The second technique is to use the Terser. The Terser allows you
             * to access field values using a path-like notation. For more information
             * on the Terser, see the example here:
             * http://hl7api.sourceforge.net/xref/ca/uhn/hl7v2/examples/ExampleUseTerser.html
             */

            // This time we just use a normal ModelClassFactory, which means we will be
            // using the standard version-specific model classes
            context.setModelClassFactory(new DefaultModelClassFactory());

            // 20169838-v23
            Message v23Message = parser.parse(v23message);
            Terser t23 = new Terser(v23Message);
            System.out.println(t23.get("/MSH-10"));

            // 20169838-v25
            Message v25Message = parser.parse(v25message);
            Terser t25 = new Terser(v25Message);
            System.out.println(t25.get("/MSH-10"));

            /*
             * Note that this second technique has one major drawback: Although 
             * message definitions are backwards compatible, some group names
             * change between versions. If you are accessing a group within
             * a complex message structure, this can cause issues.
             * 
             * This is less of an issue for some message types where groups are
             * not used much (e.g. ADT)
             */

            // This works and prints "Test Value"
            System.out.println(t23.get("/RESPONSE/ORDER_OBSERVATION/OBSERVATION(0)/OBX-5"));

            // This fails...
            // System.out.println(t25.get("/RESPONSE/ORDER_OBSERVATION/OBSERVATION(0)/OBX-5"));

            // ...because this would be required to extract the OBX-5 value from a v2.5 message
            System.out.println(t25.get("/PATIENT_RESULT/ORDER_OBSERVATION/OBSERVATION(0)/OBX-5"));

            /*
             * A third technique which may occasionally be useful is to simply use
             * a "Generic" message structure. Generic message structures can 
             * represent anything within an HL7 message, but they don't actually
             * model all of the intricacies of the structure within the message,
             * but rather just model all of the data in an unstructured way.
             */

            // Create a new context using a Generic Model Class Factory
            context = new DefaultHapiContext();
            context.setModelClassFactory(new GenericModelClassFactory());

            v25message = "MSH|^~\\&|ULTRA|TML|OLIS|OLIS|200905011130||ORU^R01|20169838-v25|T|2.5\r"
                    + "PID|||7005728^^^TML^MR||TEST^RACHEL^DIAMOND||19310313|F|||200 ANYWHERE ST^^TORONTO^ON^M6G 2T9||(416)888-8888||||||1014071185^KR\r"
                    + "PV1|1||OLIS||||OLIST^BLAKE^DONALD^THOR^^^^^921379^^^^OLIST\r"
                    + "ORC|RE||T09-100442-RET-0^^OLIS_Site_ID^ISO|||||||||OLIST^BLAKE^DONALD^THOR^^^^L^921379\r"
                    + "OBR|0||T09-100442-RET-0^^OLIS_Site_ID^ISO|RET^RETICULOCYTE COUNT^HL79901 literal|||200905011106|||||||200905011106||OLIST^BLAKE^DONALD^THOR^^^^L^921379||7870279|7870279|T09-100442|MOHLTC|200905011130||B7|F||1^^^200905011106^^R\r"
                    + "OBX|1|ST|||Test Value\r" + "NTE||Note for OBX(1)\r" + "OBX|2|ST|||Value number 2";

            // The parser will always parse this as a "GenericMessage"
            GenericMessage message = (GenericMessage) context.getPipeParser().parse(v25message);

            /* 
             * A generic message has a flat structure, so you can ask for any
             * field by only its segment name, not a complex path 
             */
            Terser t = new Terser(message);
            System.out.println(t.get("/OBX-5"));
            // Prints: Test Value

            /*
             * This technique isn't great for messages with complex structures. For
             * example, the second OBX in the message above is a part of the base structure
             * because GenericMessage has no groups.
             * 
             * It can be accessed using a new segment name (OBX2 instead of OBX)
             * but this is error prone, so use with caution.
             */
            System.out.println(t.get("/OBX2-5"));
            // Prints: Value number 2

        }

    }

    public String HapiModifyXML(String hl7message, String hl7XML, String version, String messageType)
            throws HL7Exception, IOException, InstantiationException, IllegalAccessException,
            ClassNotFoundException {

        MinLowerLayerProtocol llp = new MinLowerLayerProtocol();
        ca.uhn.hl7v2.validation.impl.DefaultValidation dv = new ca.uhn.hl7v2.validation.impl.DefaultValidation();

        ca.uhn.hl7v2.HapiContext context = new ca.uhn.hl7v2.DefaultHapiContext(dv);

        if (version.isEmpty())
            version = "2.5";
        if (messageType.isEmpty())
            version = "ADT^A01";

        // Create the MCF. We want all parsed messages to be for HL7 version 2.5,
        // despite what MSH-12 says.
        ca.uhn.hl7v2.parser.CanonicalModelClassFactory mcf = new ca.uhn.hl7v2.parser.CanonicalModelClassFactory(
                version); //TODO pass in the exact version here
        context.setModelClassFactory(mcf);

        ca.uhn.hl7v2.parser.PipeParser parser = new ca.uhn.hl7v2.parser.PipeParser(mcf);

        // Pass the MCF to the parser in its constructor

        if (messageType.contains("ADT^A01"))
            hl7XML = ReplaceADT_A01(mcf, parser, hl7message, hl7XML);
        System.out.println(hl7XML);
        //context.close();
        //               
        //               ADT_A01
        //               ADT_A02
        //               ADT_A03
        //               ADT_A05
        //               ADT_A06
        //               ADT_A09
        //               ADT_A12
        //               ADT_A15
        //               ADT_A16
        //               ADT_A17
        //               ADT_A18
        //               ADT_A20
        //               ADT_A21
        //               ADT_A24
        //               ADT_A30
        //               ADT_A37
        //               ADT_A38
        //               ADT_A39
        //               ADT_A43
        //               ADT_A45
        //               ADT_A50
        //               ADT_A52
        //               ADT_A54
        //               ADT_A60
        //               ADT_A61
        //               ADT_AXX               
        //               
        // The parser parses the v2.3 message to a "v25" structure

        // testing to get the stuff to work without manually invoking the segment
        //         for (int i = 0; i < msgNames.length; i++ ) {
        //            System.out.println(msgNames[i]);
        //            Class<? extends Structure> c = msg.getClass(msgNames[i]);
        //            Structure myInstance = (Structure) msg.get(c.getName()); 
        //                  //Class.forName(c.getName()).newInstance();
        //            Field[] fields = c.getFields();
        //            for (int j = fields.length-1; j > 0; j-- ) {
        //               System.out.println(fields[j].getName());
        //            }
        //            
        //         }

        //throwing NPE context.close();

        return hl7XML;

    }

    private String ReplaceADT_A01(CanonicalModelClassFactory mcf, PipeParser parser, String hl7message,
            String hl7XML) throws HL7Exception {

        ADT_A01 msg = new ca.uhn.hl7v2.model.v25.message.ADT_A01(mcf); //(ca.uhn.hl7v2.model.v25.message.ORU_R01) parser.parse(v23message);

        msg = (ca.uhn.hl7v2.model.v25.message.ADT_A01) parser.parse(hl7message);

        hl7XML = HL7XMLReplace(msg.getMSH().getNames(), hl7XML, "MSH");
        System.out.println("past MSH");
        hl7XML = HL7XMLReplace(msg.getSFT().getNames(), hl7XML, "SFT");
        hl7XML = HL7XMLReplace(msg.getEVN().getNames(), hl7XML, "EVN");
        hl7XML = HL7XMLReplace(msg.getPID().getNames(), hl7XML, "PID");
        hl7XML = HL7XMLReplace(msg.getPD1().getNames(), hl7XML, "PD1");
        hl7XML = HL7XMLReplace(msg.getROL().getNames(), hl7XML, "ROL");
        hl7XML = HL7XMLReplace(msg.getNK1().getNames(), hl7XML, "NK1");
        hl7XML = HL7XMLReplace(msg.getPV1().getNames(), hl7XML, "PV1");
        hl7XML = HL7XMLReplace(msg.getPV2().getNames(), hl7XML, "PV2");
        hl7XML = HL7XMLReplace(msg.getAL1().getNames(), hl7XML, "AL1");
        hl7XML = HL7XMLReplace(msg.getDB1().getNames(), hl7XML, "DB1");
        hl7XML = HL7XMLReplace(msg.getDRG().getNames(), hl7XML, "DRG");
        hl7XML = HL7XMLReplace(msg.getACC().getNames(), hl7XML, "ACC");
        hl7XML = HL7XMLReplace(msg.getPDA().getNames(), hl7XML, "PDA");
        hl7XML = HL7XMLReplace(msg.getDG1().getNames(), hl7XML, "DG1");
        //hl7XML = HL7XMLReplace(msg.getPR1().getNames(), hl7XML, "PR1");

        hl7XML = HL7XMLReplace(msg.getGT1().getNames(), hl7XML, "GT1");

        // only need to do this once for N insurances
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN1().getNames(), hl7XML, "IN1");
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN2().getNames(), hl7XML, "IN2");
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN3().getNames(), hl7XML, "IN3");

        return hl7XML;
    }

    public String ReplaceADT_A01Msg(ca.uhn.hl7v2.model.v25.message.ADT_A01 msg, String hl7XML) throws HL7Exception {

        hl7XML = HL7XMLReplace(msg.getMSH().getNames(), hl7XML, "MSH");
        hl7XML = HL7XMLReplace(msg.getSFT().getNames(), hl7XML, "SFT");
        hl7XML = HL7XMLReplace(msg.getEVN().getNames(), hl7XML, "EVN");
        hl7XML = HL7XMLReplace(msg.getPID().getNames(), hl7XML, "PID");
        hl7XML = HL7XMLReplace(msg.getPD1().getNames(), hl7XML, "PD1");
        hl7XML = HL7XMLReplace(msg.getROL().getNames(), hl7XML, "ROL");
        hl7XML = HL7XMLReplace(msg.getNK1().getNames(), hl7XML, "NK1");
        hl7XML = HL7XMLReplace(msg.getPV1().getNames(), hl7XML, "PV1");
        hl7XML = HL7XMLReplace(msg.getPV2().getNames(), hl7XML, "PV2");
        hl7XML = HL7XMLReplace(msg.getAL1().getNames(), hl7XML, "AL1");
        hl7XML = HL7XMLReplace(msg.getDB1().getNames(), hl7XML, "DB1");
        hl7XML = HL7XMLReplace(msg.getDRG().getNames(), hl7XML, "DRG");
        hl7XML = HL7XMLReplace(msg.getACC().getNames(), hl7XML, "ACC");
        hl7XML = HL7XMLReplace(msg.getPDA().getNames(), hl7XML, "PDA");
        hl7XML = HL7XMLReplace(msg.getDG1().getNames(), hl7XML, "DG1");
        //hl7XML = HL7XMLReplace(msg.getPR1().getNames(), hl7XML, "PR1");

        hl7XML = HL7XMLReplace(msg.getGT1().getNames(), hl7XML, "GT1");

        // only need to do this once for N insurances
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN1().getNames(), hl7XML, "IN1");
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN2().getNames(), hl7XML, "IN2");
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN3().getNames(), hl7XML, "IN3");

        return hl7XML;
    }

    private String ReplaceADT_AXX(CanonicalModelClassFactory mcf, PipeParser parser, String hl7message,
            String hl7XML) throws HL7Exception {

        ADT_AXX msg = new ca.uhn.hl7v2.model.v25.message.ADT_AXX(mcf); //(ca.uhn.hl7v2.model.v25.message.ORU_R01) parser.parse(v23message);

        msg = (ca.uhn.hl7v2.model.v25.message.ADT_AXX) parser.parse(hl7message);

        hl7XML = HL7XMLReplace(msg.getMSH().getNames(), hl7XML, "MSH");
        hl7XML = HL7XMLReplace(msg.getSFT().getNames(), hl7XML, "SFT");
        hl7XML = HL7XMLReplace(msg.getEVN().getNames(), hl7XML, "EVN");
        hl7XML = HL7XMLReplace(msg.getPID().getNames(), hl7XML, "PID");
        hl7XML = HL7XMLReplace(msg.getPD1().getNames(), hl7XML, "PD1");
        hl7XML = HL7XMLReplace(msg.getROL().getNames(), hl7XML, "ROL");
        hl7XML = HL7XMLReplace(msg.getNK1().getNames(), hl7XML, "NK1");
        hl7XML = HL7XMLReplace(msg.getPV1().getNames(), hl7XML, "PV1");
        hl7XML = HL7XMLReplace(msg.getPV2().getNames(), hl7XML, "PV2");
        hl7XML = HL7XMLReplace(msg.getAL1().getNames(), hl7XML, "AL1");
        hl7XML = HL7XMLReplace(msg.getDB1().getNames(), hl7XML, "DB1");
        hl7XML = HL7XMLReplace(msg.getDRG().getNames(), hl7XML, "DRG");
        hl7XML = HL7XMLReplace(msg.getACC().getNames(), hl7XML, "ACC");
        hl7XML = HL7XMLReplace(msg.getPDA().getNames(), hl7XML, "PDA");
        hl7XML = HL7XMLReplace(msg.getDG1().getNames(), hl7XML, "DG1");
        hl7XML = HL7XMLReplace(msg.getOBX().getNames(), hl7XML, "OBX");
        hl7XML = HL7XMLReplace(msg.getMRG().getNames(), hl7XML, "MRG");
        //hl7XML = HL7XMLReplace(msg.getPR1().getNames(), hl7XML, "PR1");

        hl7XML = HL7XMLReplace(msg.getGT1().getNames(), hl7XML, "GT1");

        // only need to do this once for N insurances
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN1().getNames(), hl7XML, "IN1");
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN2().getNames(), hl7XML, "IN2");
        hl7XML = HL7XMLReplace(msg.getINSURANCE(0).getIN3().getNames(), hl7XML, "IN3");

        return hl7XML;
    }

    public String HL7XMLReplace(String[] names, String hl7XML, String segment) {

        for (int i = names.length - 1; i > 0; i--) {
            //System.out.println("MSH" + i + "Field name:" + mshNames[i]);   
            hl7XML = hl7XML.replaceAll(segment + "." + i, names[i].replaceAll(" ", "").replaceAll("'", "")
                    .replaceAll(",", "").replaceAll("/", "").replaceAll(":", ""));
        }
        return hl7XML;

    }

    public String XMLtoJSON(String hl7XML) {

        JSONObject xmlJSONObj = XML.toJSONObject(hl7XML);
        int PRETTY_PRINT_INDENT_FACTOR = 4;
        String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
        //System.out.println(jsonPrettyPrintString);

        return jsonPrettyPrintString;

    }

}

class MyTypeAdapter<T> extends TypeAdapter<T> {

    @Override
    public void write(JsonWriter writer, T obj) throws IOException {
        if (obj == null) {
            writer.nullValue();
            return;
        }
        writer.value(obj.toString());

    }

    @Override
    public T read(JsonReader in) throws IOException {
        // TODO Auto-generated method stub
        return null;
    }
}