/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * 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 part of dcm4che, an implementation of DICOM(TM) in * Java(TM), hosted at https://github.com/gunterze/dcm4che. * * The Initial Developer of the Original Code is * Agfa Healthcare. * Portions created by the Initial Developer are Copyright (C) 2012 * the Initial Developer. All Rights Reserved. * * Contributor(s): * See @authors listed below * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.dcm4che3.hl7; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.EnumSet; import java.util.StringTokenizer; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; /** * @author Gunter Zeilinger <gunterze@gmail.com> * */ public class HL7Parser { private static final String NAMESPACE = "http://aurora.regenstrief.org/xhl7"; private String namespace = ""; private final ContentHandler ch; private final AttributesImpl atts = new AttributesImpl(); private final EnumSet<Delimiter> open = EnumSet.noneOf(Delimiter.class); private String delimiters; public HL7Parser(ContentHandler ch) { this.ch = ch; } public final boolean isIncludeNamespaceDeclaration() { return namespace == NAMESPACE; } public final void setIncludeNamespaceDeclaration(boolean includeNameSpaceDeclaration) { this.namespace = includeNameSpaceDeclaration ? NAMESPACE : ""; } public void parse(Reader reader) throws IOException, SAXException { parse(reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader)); } public void parse(BufferedReader reader) throws IOException, SAXException { startDocument(); delimiters = Delimiter.DEFAULT; String line; while ((line = reader.readLine()) != null) { line = line.trim(); if(line.length() == 0) continue; if (line.length() < 3) throw new IOException ("Segment to short: " + line); String seg = line.substring(0, 3); String[] tks; int tkindex = 0; if (isHeaderSegment(line)) { if (line.length() < 8) throw new IOException ("Header Segment to short: " + line); seg = line.substring(0, 3); setDelimiters(line.substring(3, 8)); tks = tokenize(line.substring(8)); } else { tks = tokenize(line); seg = tks[tkindex++]; } startElement(seg); while (tkindex < tks.length) { String tk = tks[tkindex++]; Delimiter d = delimiter(tk); if (d != null) { if (d != Delimiter.escape) { endElement(d); startElement(d); continue; } if (tks.length > tkindex+1 && tks[tkindex+1].equals(tk)) { tk = tks[tkindex++]; int e = escapeIndex(tk); if (e >= 0) { ch.characters(delimiters.toCharArray(), e, 1); } else { startElement(Delimiter.escape.name()); ch.characters(tk.toCharArray(), 0, tk.length()); endElement(Delimiter.escape.name()); } tkindex++; continue; } } ch.characters(tk.toCharArray(), 0, tk.length()); } endElement(Delimiter.field); endElement(seg); } endDocument(); } private boolean isHeaderSegment(String line) { return (line.startsWith("MSH") || line.startsWith("BHS") || line.startsWith("FHS")); } private void startDocument() throws SAXException { ch.startDocument(); addAttribute("xml-space", "preserved"); startElement("hl7"); } private void endDocument() throws SAXException { endElement("hl7"); ch.endDocument(); } private void setDelimiters(String delimiters) { Delimiter[] a = Delimiter.values(); for (int i = 0; i < a.length; i++) addAttribute(a[i].attribute(), delimiters.substring(i,i+1)); this.delimiters = delimiters; } private void addAttribute(String name, String value) { atts.addAttribute(namespace, name, name, "NMTOKEN", value); } private Delimiter delimiter(String tk) { if (tk.length() != 1) return null; int index = delimiters.indexOf(tk.charAt(0)); return index >= 0 ? Delimiter.values()[index] : null; } private int escapeIndex(String tk) { return tk.length() != 1 ? Delimiter.ESCAPE.indexOf(tk.charAt(0)) : -1; } private String[] tokenize(String s) { StringTokenizer stk = new StringTokenizer(s, delimiters, true); String[] tks = new String[stk.countTokens()]; for (int i = 0; i < tks.length; i++) tks[i] = stk.nextToken(); return tks; } private void startElement(Delimiter d) throws SAXException { startElement(d.name()); open.add(d); } private void startElement(String name) throws SAXException { ch.startElement(namespace, name, name, atts); atts.clear(); } private void endElement(Delimiter delimiter) throws SAXException { Delimiter d = Delimiter.escape; do if (open.remove(d = d.parent())) endElement(d.name()); while (d != delimiter); } private void endElement(String name) throws SAXException { ch.endElement(namespace, name, name); } }