package ch.elexis.hl7;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ibm.icu.text.CharsetDetector;
import com.ibm.icu.text.CharsetMatch;
import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.GenericMessage.V23;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.parser.Parser;
import ca.uhn.hl7v2.parser.PipeParser;
import ca.uhn.hl7v2.util.Hl7InputStreamMessageStringIterator;
import ca.uhn.hl7v2.validation.impl.NoValidation;
import ch.elexis.core.jdt.Nullable;
import ch.elexis.hl7.v26.Messages;
import ch.elexis.hl7.v2x.HL7ReaderV21;
import ch.elexis.hl7.v2x.HL7ReaderV22;
import ch.elexis.hl7.v2x.HL7ReaderV23;
import ch.elexis.hl7.v2x.HL7ReaderV231;
import ch.elexis.hl7.v2x.HL7ReaderV24;
import ch.elexis.hl7.v2x.HL7ReaderV25;
import ch.elexis.hl7.v2x.HL7ReaderV251;
import ch.elexis.hl7.v2x.HL7ReaderV26;
public enum HL7ReaderFactory {
INSTANCE;
protected List<Message> messageList;
private static Logger logger = LoggerFactory.getLogger(HL7ReaderFactory.class);
public List<HL7Reader> getReader(File file) throws IOException{
checkClassLoader();
messageList = new ArrayList<Message>();
return load(file);
}
public @Nullable HL7Reader getReader(String message) throws IOException {
checkClassLoader();
messageList = new ArrayList<Message>();
try {
return loadMessage(message);
} catch (HL7Exception e) {
throw new IOException(HL7Exception.class.getName()+": "+e.getMessage());
}
}
private List<HL7Reader> load(File file) throws IOException{
if (!file.canRead()) {
throw new IOException(MessageFormat
.format(Messages.getString("HL7Reader_CannotReadFile"), file.getAbsolutePath()));
}
List<HL7Reader> ret = new ArrayList<HL7Reader>();
try (InputStream inputStream = getFileInputStream(file)) {
// HAPI utility class will iterate over the messages which appear over an InputStream
Hl7InputStreamMessageStringIterator stringIterator =
new Hl7InputStreamMessageStringIterator(inputStream);
Parser p = new PipeParser();
p.setValidationContext(new NoValidation());
while (stringIterator.hasNext()) {
String next = stringIterator.next();
next = assureSaveMSH9Access(next);
Message hl7Message = p.parse(next);
messageList.add(hl7Message);
ret.add(getReaderForMessage(hl7Message));
}
return ret;
} catch (Exception ex) {
throw new IOException(ex);
}
}
private void checkClassLoader(){
ClassLoader modelLoader = V23.class.getClassLoader();
ClassLoader parserLoader = Parser.class.getClassLoader();
if (modelLoader != parserLoader) {
throw new IllegalStateException("Model and Parser loaded by different ClassLoader");
}
}
private InputStream getFileInputStream(File file) throws IOException{
byte[] bytes = Files.readAllBytes(file.toPath());
CharsetDetector detector = new CharsetDetector();
detector.setText(bytes);
CharsetMatch match = detector.detect();
if (match != null) {
if (match.getName().contains("IBM424")) {
logger.warn(
"Reading HL7 file " + file.getAbsolutePath() + " with unsupported encoding "
+ match.getName() + " - trying to use ISO-8859-1 instead");
return new ByteArrayInputStream(new String(bytes, "ISO-8859-1").getBytes());
}
logger.info("Reading HL7 file " + file.getAbsolutePath() + " encoded " + match.getName()
+ " language " + match.getLanguage());
return new ByteArrayInputStream(match.getString().getBytes());
}
return new ByteArrayInputStream(bytes);
}
private String assureSaveMSH9Access(String hl7Message){
String separator = "\r";
String[] splitted = hl7Message.split(separator);
if (splitted.length < 2) {
separator = "\n";
splitted = hl7Message.split(separator);
}
if (splitted.length < 2) {
throw new IllegalArgumentException("Could not split message");
}
String[] mshPart = splitted[0].split("\\|", -1);
if (!mshPart[8].equals("ORU^R01") && !mshPart[11].startsWith("2.5")) {
mshPart[8] = "ORU^R01";
splitted[0] = joinStrings(mshPart, "|");
}
// 2.3.2 is no proper Hl7 version and therefore needs to be handled as version 2.3
if (mshPart[11].equals("2.3.2")) {
mshPart[11] = "2.3";
splitted[0] = joinStrings(mshPart, "|");
}
// #2747 BugFix as LabCube_SpotChemD sends occasionally SN which is not allowed for version
// 2.2
if (mshPart[11].equals("2.2")) {
for (int i = 0; i < splitted.length; i++) {
if (splitted[i].startsWith("OBX")) {
String[] obxPart = splitted[i].split("\\|", -1);
if (obxPart[2].equals("SN")) {
obxPart[2] = "NM";
splitted[i] = joinStrings(obxPart, "|");
}
}
}
}
if (mshPart[11].equals("2.7.1")) {
mshPart[11] = "2.6";
splitted[0] = joinStrings(mshPart, "|");
}
return joinStrings(splitted, separator);
}
private String joinStrings(String[] array, String separator){
StringBuilder builder = new StringBuilder();
for (String s : array) {
if (builder.length() != 0) {
builder.append(separator);
}
builder.append(s);
}
return builder.toString();
}
private HL7Reader loadMessage(String message) throws HL7Exception{
Parser p = new PipeParser();
p.setValidationContext(new NoValidation());
Message hl7Msg = p.parse(message);
messageList.add(hl7Msg);
return getReaderForMessage(hl7Msg);
}
private HL7Reader getReaderForMessage(Message message){
String version = message.getVersion();
if (version.equals("2.1")) {
return new HL7ReaderV21(message);
} else if (version.equals("2.2")) {
return new HL7ReaderV22(message);
}
if (version.equals("2.3")) {
return new HL7ReaderV23(message);
}
if (version.equals("2.3.1")) {
return new HL7ReaderV231(message);
}
if (version.equals("2.4")) {
return new HL7ReaderV24(message);
}
if (version.equals("2.5")) {
return new HL7ReaderV25(message);
}
if (version.equals("2.5.1")) {
return new HL7ReaderV251(message);
}
if (version.equals("2.6")) {
return new HL7ReaderV26(message);
}
return null;
}
}