All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.linuxforhealth.hl7.message.HL7MessageModel Maven / Gradle / Ivy

/*
 * (C) Copyright IBM Corp. 2020, 2022
 *
 * SPDX-License-Identifier: Apache-2.0
 */
package io.github.linuxforhealth.hl7.message;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.Message;
import io.github.linuxforhealth.api.FHIRResourceTemplate;
import io.github.linuxforhealth.api.MessageEngine;
import io.github.linuxforhealth.api.MessageTemplate;
import io.github.linuxforhealth.hl7.parsing.HL7DataExtractor;
import io.github.linuxforhealth.hl7.parsing.HL7HapiParser;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HL7MessageModel implements MessageTemplate {

    private List resources;
    private String messageName;
    private static final Logger LOGGER = LoggerFactory.getLogger(HL7MessageModel.class);

    @JsonCreator
    public HL7MessageModel(@JsonProperty("messageName") String messageName,
            @JsonProperty("resources") List resources) {
        this.messageName = messageName;
        this.resources = new ArrayList<>();
        if (resources != null && !resources.isEmpty()) {
            this.resources.addAll(resources);
        }

    }

    private void handleException(Exception e) {
        StackTraceElement[] stackTrace = e.getStackTrace();
        StringBuilder classAndStack = new StringBuilder();
        classAndStack.append(e.getClass() + "\n");
        for (int i = 0; i < stackTrace.length; i++) {
            classAndStack.append(stackTrace[i] + "\n");
        }
        LOGGER.error("Error transforming HL7 message. {}", classAndStack);
    }

    public String convert(String message, MessageEngine engine) throws IOException {
        Preconditions.checkArgument(StringUtils.isNotBlank(message),
                "Input Hl7 message cannot be blank");
        HL7HapiParser hparser = null;
        try {
            hparser = new HL7HapiParser();

            Message hl7message = hparser.getParser().parse(message);
            Bundle bundle = convert(hl7message, engine);
            return engine.getFHIRContext().encodeResourceToString(bundle);

        } catch (HL7Exception e) {
            throw new IllegalArgumentException("Cannot parse the message.", e);
        } finally {
            if (hparser != null) {
                hparser.getContext().close();
            }
        }

    }

    @Override
    public Bundle convert(Message message, MessageEngine engine) {
        Preconditions.checkArgument(message != null, "Input Hl7 message cannot be null");
        Preconditions.checkArgument(engine != null, "MessageEngine cannot be null");

        HL7DataExtractor hl7DTE = new HL7DataExtractor(message);
        HL7MessageData dataSource = new HL7MessageData(hl7DTE);

        Bundle bundle = null;

        // Catch any exceptions and log them without the message.
        // NOTE: We have seen PHI in these exception messages.
        try {
            bundle = engine.transform(dataSource, this.getResources(), new HashMap<>());
            deduplicate(bundle);  // Bundle is passed by reference and may be modified
            engine.getFHIRContext().validate(bundle);

        } catch (Exception e) {
            // Print stack class and trace without the error message.
            handleException(e);
        }

        return bundle;
    }

    @Override
    public String getMessageName() {
        return messageName;
    }

    public void setMessageName(String messageName) {
        this.messageName = messageName;
    }

    @Override
    public List getResources() {
        return new ArrayList<>(resources);
    }

    // General deduplication utility. Currently only Organizations.
    // Bundle is passed by reference and may be modified
    private void deduplicate(Bundle bundle) {
        List entries = bundle.getEntry();
        Iterator i = entries.iterator();
        while (i.hasNext()) {
            BundleEntryComponent entry = i.next(); // Safe traversal for removal
            // Currently only looking for duplicate Organizations, because their Urls and Id's are created from org data.
            if (entry.getFullUrl().startsWith("Organization/") && duplicateFound(entry, entries)) {
                i.remove();  // Safe removal
            }
        }
    }

    // Determine if an entry has duplicates by counting all the entries which have the same Url.
    // If count > 1 then it has duplicates.  (It will be counted, too, as 1.)
    private boolean duplicateFound(BundleEntryComponent entry, List entries) {
        String targetUrl = entry.getFullUrl();
        Integer foundCount = 0;
        for (BundleEntryComponent component : entries) {
            if (component.getFullUrl().equals(targetUrl)) {
                foundCount++;
            }
        }
        return foundCount > 1;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy