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.Serializable; 042import java.io.UnsupportedEncodingException; 043import java.text.ParsePosition; 044import java.text.SimpleDateFormat; 045import java.util.Arrays; 046import java.util.Date; 047import java.util.Random; 048import java.util.concurrent.atomic.AtomicInteger; 049 050/** 051 * @author Gunter Zeilinger <gunterze@gmail.com> 052 * 053 */ 054public class HL7Segment implements Serializable { 055 056 private static final long serialVersionUID = 2268883954083242976L; 057 private static final AtomicInteger nextMessageControlID = 058 new AtomicInteger(new Random().nextInt()); 059 060 private final char fieldSeparator; 061 private final String encodingCharacters; 062 private String[] fields; 063 064 public HL7Segment(int size, char fieldSeparator, String encodingCharacters) { 065 if (size <= 0) 066 throw new IllegalArgumentException("size: " + size); 067 this.fieldSeparator = fieldSeparator; 068 this.encodingCharacters = encodingCharacters; 069 this.fields = new String[size]; 070 } 071 072 public HL7Segment(int size) { 073 this(size, '|', "^~\\&"); 074 } 075 076 public HL7Segment(String s, char fieldSeparator, String encodingCharacters) { 077 this.fieldSeparator = fieldSeparator; 078 this.encodingCharacters = encodingCharacters; 079 this.fields = split(s, fieldSeparator); 080 } 081 082 public final char getFieldSeparator() { 083 return fieldSeparator; 084 } 085 086 public final char getComponentSeparator() { 087 return encodingCharacters.charAt(0); 088 } 089 090 public final char getRepetitionSeparator() { 091 return encodingCharacters.charAt(1); 092 } 093 094 public final char getEscapeCharacter() { 095 return encodingCharacters.charAt(2); 096 } 097 098 public final char getSubcomponentSeparator() { 099 return encodingCharacters.charAt(3); 100 } 101 102 public final String getEncodingCharacters() { 103 return encodingCharacters; 104 } 105 106 public void setField(int index, String value) { 107 if (index >= fields.length) 108 fields = Arrays.copyOf(fields, index+1); 109 fields[index] = value; 110 } 111 112 public String getField(int index, String defVal) { 113 String val = index < fields.length ? fields[index] : null; 114 return val != null && !val.isEmpty() ? val : defVal; 115 } 116 117 public int size() { 118 return fields.length; 119 } 120 121 public String getSendingApplicationWithFacility() { 122 return getField(2, "") + '^' + getField(3, ""); 123 } 124 125 public void setSendingApplicationWithFacility(String s) { 126 String[] ss = split(s, '^'); 127 setField(2, ss[0]); 128 if (ss.length > 1) 129 setField(3, ss[1]); 130 } 131 132 public String getReceivingApplicationWithFacility() { 133 return getField(4, "") + '^' + getField(5, ""); 134 } 135 136 public void setReceivingApplicationWithFacility(String s) { 137 String[] ss = split(s, '^'); 138 setField(4, ss[0]); 139 if (ss.length > 1) 140 setField(5, ss[1]); 141 } 142 143 public String getMessageType() { 144 String s = getField(8, "").replace(getComponentSeparator(), '^'); 145 int end = s.indexOf('^', s.indexOf('^') + 1); 146 return end > 0 ? s.substring(0, end) : s; 147 } 148 149 public String toString() { 150 return concat(fields, fieldSeparator); 151 } 152 153 public static String concat(String[] ss, char delim) { 154 int n = ss.length; 155 if (n == 0) 156 return ""; 157 if (n == 1) { 158 String s = ss[0]; 159 return s != null ? s : ""; 160 } 161 int len = n - 1; 162 for (String s : ss) 163 if (s != null) 164 len += s.length(); 165 char[] cs = new char[len]; 166 for (int i = 0, off = 0; i < n; ++i) { 167 if (i != 0) 168 cs[off++] = delim; 169 String s = ss[i]; 170 if (s != null) { 171 int l = s.length(); 172 s.getChars(0, l, cs, off); 173 off += l; 174 } 175 } 176 return new String(cs); 177 } 178 179 public static String[] split(String s, char delim) { 180 int count = 1; 181 int delimPos = -1; 182 while ((delimPos = s.indexOf(delim, delimPos+1)) >= 0) 183 count++; 184 185 if (count == 1) 186 return new String[] { s }; 187 188 String[] ss = new String[count]; 189 int delimPos2 = s.length(); 190 while (--count >= 0) { 191 delimPos = s.lastIndexOf(delim, delimPos2-1); 192 ss[count] = s.substring(delimPos+1, delimPos2); 193 delimPos2 = delimPos; 194 } 195 return ss; 196 } 197 198 public static HL7Segment parseMSH(byte[] b, int size) { 199 return parseMSH(b, size, new ParsePosition(0)); 200 } 201 202 public static HL7Segment parseMSH(byte[] b, int size, ParsePosition pos) { 203 String s = parse(b, size, pos, null); 204 if (s.length() < 8) 205 throw new IllegalArgumentException("Invalid MSH Segment: " + s); 206 return new HL7Segment(s, s.charAt(3), s.substring(4,8)); 207 } 208 209 static HL7Segment parse(byte[] b, int size, ParsePosition pos, 210 char fieldSeparator, String encodingCharacters, String charsetName) { 211 String s = parse(b, size, pos, charsetName); 212 return s != null 213 ? new HL7Segment(s, fieldSeparator, encodingCharacters) 214 : null; 215 } 216 217 private static String parse(byte[] b, int size, ParsePosition pos, 218 String charsetName) { 219 int off = pos.getIndex(); 220 int end = off; 221 while (end < size && b[end] != '\r' && b[end] != '\n') 222 end++; 223 224 int len = end - off; 225 if (len == 0) 226 return null; 227 228 if (++end < size && (b[end] == '\r' || b[end] == '\n')) 229 end++; 230 231 pos.setIndex(end); 232 try { 233 return charsetName != null 234 ? new String(b, off, len, charsetName) 235 : new String(b, off, len); 236 } catch (UnsupportedEncodingException e) { 237 throw new IllegalArgumentException("charsetName: " + charsetName); 238 } 239 } 240 241 public static String nextMessageControlID() { 242 return Integer.toString( 243 nextMessageControlID.getAndIncrement() & 0x7FFFFFFF); 244 } 245 246 public static String timeStamp(Date date) { 247 return new SimpleDateFormat("yyyyMMddHHmmss.SSS").format(date); 248 } 249 250 public static HL7Segment makeMSH() { 251 return makeMSH(21, '|', "^~\\&"); 252 } 253 254 public static HL7Segment makeMSH(int size, char fieldSeparator, String encodingCharacters) { 255 HL7Segment msh = new HL7Segment(size, fieldSeparator, encodingCharacters); 256 msh.setField(0, "MSH"); 257 msh.setField(1, encodingCharacters); 258 msh.setField(6, timeStamp(new Date())); 259 msh.setField(9, nextMessageControlID()); 260 msh.setField(10, "P"); 261 msh.setField(11, "2.5"); 262 return msh; 263 } 264}