001/* ***** BEGIN LICENSE BLOCK ***** 002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 003 * 004 * The contents of this file are subject to the Mozilla Public License Version 005 * 1.1 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * http://www.mozilla.org/MPL/ 008 * 009 * Software distributed under the License is distributed on an "AS IS" basis, 010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 011 * for the specific language governing rights and limitations under the 012 * License. 013 * 014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 015 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 016 * 017 * The Initial Developer of the Original Code is 018 * Agfa Healthcare. 019 * Portions created by the Initial Developer are Copyright (C) 2012 020 * the Initial Developer. All Rights Reserved. 021 * 022 * Contributor(s): 023 * See @authors listed below 024 * 025 * Alternatively, the contents of this file may be used under the terms of 026 * either the GNU General Public License Version 2 or later (the "GPL"), or 027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 028 * in which case the provisions of the GPL or the LGPL are applicable instead 029 * of those above. If you wish to allow use of your version of this file only 030 * under the terms of either the GPL or the LGPL, and not to allow others to 031 * use your version of this file under the terms of the MPL, indicate your 032 * decision by deleting the provisions above and replace them with the notice 033 * and other provisions required by the GPL or the LGPL. If you do not delete 034 * the provisions above, a recipient may use your version of this file under 035 * the terms of any one of the MPL, the GPL or the LGPL. 036 * 037 * ***** END LICENSE BLOCK ***** */ 038 039package org.dcm4che3.hl7; 040 041import java.io.BufferedReader; 042import java.io.IOException; 043import java.io.Reader; 044import java.util.EnumSet; 045import java.util.StringTokenizer; 046 047import org.xml.sax.ContentHandler; 048import org.xml.sax.SAXException; 049import org.xml.sax.helpers.AttributesImpl; 050 051/** 052 * @author Gunter Zeilinger <gunterze@gmail.com> 053 * 054 */ 055public class HL7Parser { 056 057 private static final String NAMESPACE = "http://aurora.regenstrief.org/xhl7"; 058 059 private String namespace = ""; 060 private final ContentHandler ch; 061 private final AttributesImpl atts = new AttributesImpl(); 062 private final EnumSet<Delimiter> open = EnumSet.noneOf(Delimiter.class); 063 private String delimiters; 064 065 public HL7Parser(ContentHandler ch) { 066 this.ch = ch; 067 } 068 069 public final boolean isIncludeNamespaceDeclaration() { 070 return namespace == NAMESPACE; 071 } 072 073 public final void setIncludeNamespaceDeclaration(boolean includeNameSpaceDeclaration) { 074 this.namespace = includeNameSpaceDeclaration ? NAMESPACE : ""; 075 } 076 077 public void parse(Reader reader) throws IOException, SAXException { 078 parse(reader instanceof BufferedReader 079 ? (BufferedReader) reader 080 : new BufferedReader(reader)); 081 } 082 083 public void parse(BufferedReader reader) throws IOException, SAXException { 084 startDocument(); 085 delimiters = Delimiter.DEFAULT; 086 String line; 087 while ((line = reader.readLine()) != null) { 088 line = line.trim(); 089 if(line.length() == 0) 090 continue; 091 092 if (line.length() < 3) 093 throw new IOException ("Segment to short: " + line); 094 095 String seg = line.substring(0, 3); 096 String[] tks; 097 int tkindex = 0; 098 if (isHeaderSegment(line)) { 099 if (line.length() < 8) 100 throw new IOException ("Header Segment to short: " + line); 101 102 seg = line.substring(0, 3); 103 setDelimiters(line.substring(3, 8)); 104 tks = tokenize(line.substring(8)); 105 } else { 106 tks = tokenize(line); 107 seg = tks[tkindex++]; 108 } 109 startElement(seg); 110 while (tkindex < tks.length) { 111 String tk = tks[tkindex++]; 112 Delimiter d = delimiter(tk); 113 if (d != null) { 114 if (d != Delimiter.escape) { 115 endElement(d); 116 startElement(d); 117 continue; 118 } 119 if (tks.length > tkindex+1 && tks[tkindex+1].equals(tk)) { 120 tk = tks[tkindex++]; 121 int e = escapeIndex(tk); 122 if (e >= 0) { 123 ch.characters(delimiters.toCharArray(), e, 1); 124 } else { 125 startElement(Delimiter.escape.name()); 126 ch.characters(tk.toCharArray(), 0, tk.length()); 127 endElement(Delimiter.escape.name()); 128 } 129 tkindex++; 130 continue; 131 } 132 } 133 ch.characters(tk.toCharArray(), 0, tk.length()); 134 } 135 endElement(Delimiter.field); 136 endElement(seg); 137 } 138 endDocument(); 139 } 140 141 private boolean isHeaderSegment(String line) { 142 return (line.startsWith("MSH") 143 || line.startsWith("BHS") 144 || line.startsWith("FHS")); 145 } 146 147 private void startDocument() throws SAXException { 148 ch.startDocument(); 149 addAttribute("xml-space", "preserved"); 150 startElement("hl7"); 151 } 152 153 private void endDocument() throws SAXException { 154 endElement("hl7"); 155 ch.endDocument(); 156 } 157 158 private void setDelimiters(String delimiters) { 159 Delimiter[] a = Delimiter.values(); 160 for (int i = 0; i < a.length; i++) 161 addAttribute(a[i].attribute(), delimiters.substring(i,i+1)); 162 this.delimiters = delimiters; 163 } 164 165 private void addAttribute(String name, String value) { 166 atts.addAttribute(namespace, name, name, "NMTOKEN", value); 167 } 168 169 private Delimiter delimiter(String tk) { 170 if (tk.length() != 1) 171 return null; 172 173 int index = delimiters.indexOf(tk.charAt(0)); 174 return index >= 0 ? Delimiter.values()[index] : null; 175 } 176 177 private int escapeIndex(String tk) { 178 return tk.length() != 1 ? Delimiter.ESCAPE.indexOf(tk.charAt(0)) : -1; 179 } 180 181 private String[] tokenize(String s) { 182 StringTokenizer stk = new StringTokenizer(s, delimiters, true); 183 String[] tks = new String[stk.countTokens()]; 184 for (int i = 0; i < tks.length; i++) 185 tks[i] = stk.nextToken(); 186 187 return tks; 188 } 189 190 private void startElement(Delimiter d) throws SAXException { 191 startElement(d.name()); 192 open.add(d); 193 } 194 195 private void startElement(String name) throws SAXException { 196 ch.startElement(namespace, name, name, atts); 197 atts.clear(); 198 } 199 200 private void endElement(Delimiter delimiter) throws SAXException { 201 Delimiter d = Delimiter.escape; 202 do 203 if (open.remove(d = d.parent())) 204 endElement(d.name()); 205 while (d != delimiter); 206 } 207 208 private void endElement(String name) throws SAXException { 209 ch.endElement(namespace, name, name); 210 } 211 212}