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}