View Javadoc
1   /*
2    * Copyright 2011 InterComponentWare AG.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.openehealth.ipf.platform.camel.ihe.mllp.core;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  import java.util.UUID;
21  
22  /**
23   * Helper methods for segment fragmentation, unsolicited request fragmentation,
24   * and interactive response continuation in HL7v2-based transactions.
25   *
26   * @author Dmytro Rud
27   */
28  public class FragmentationUtils {
29  
30      /**
31       * Splits the given String at occurrences of the given character.
32       */
33      public static List<String> splitString(String s, char c) {
34          List<String> result = new ArrayList<>();
35          int startPos = 0;
36          while (true) {
37              int pos = s.indexOf(c, startPos);
38              if (pos == -1) {
39                  break;
40              }
41              result.add(s.substring(startPos, pos));
42              startPos = pos + 1;
43          }
44          if (startPos != s.length()) {
45              result.add(s.substring(startPos, s.length()));
46          }
47          return result;
48      }
49  
50  
51      /**
52       * Ensures that all segments in the given HL7 message string representation
53       * are not longer than the given value (-1 means positive infinity).
54       * If needed, splits long segments by means of ADD segments, as described
55       * in paragraph 2.10.2.1 of the HL7 v.2.5 specification.
56       * <p>
57       * <code>'\r'<code> characters are not considered in the length computation.
58       * @param message
59       *      string representation of the source HL7 message.
60       * @param maxLength
61       *      maximal segment length, must be either -1 or greater than 4.
62       * @return
63       *      string representation of a semantically equivalent message,
64       *      whose segments are not longer than the given value.
65       */
66      public static String ensureMaximalSegmentsLength(String message, int maxLength) {
67          if (maxLength == -1) {
68              return message;
69          }
70          if (maxLength <= 4) {
71              throw new IllegalArgumentException("maximal length must be greater than 4");
72          }
73          List<String> segments = splitString(message, '\r');
74  
75          // check whether we have to do anything
76          boolean needProcess = false;
77          for (String segment : segments) {
78              if (segment.length() > maxLength) {
79                  needProcess = true;
80                  break;
81              }
82          }
83          if ( ! needProcess) {
84              return message;
85          }
86  
87          // process segments
88          StringBuilder sb = new StringBuilder();
89          String prefix = "ADD" + message.charAt(3);
90          int restLength = maxLength - prefix.length();
91          for (String segment : segments) {
92              // short segment
93              if (segment.length() <= maxLength) {
94                  sb.append(segment).append('\r');
95                  continue;
96              }
97  
98              // first part of the long segment
99              sb.append(segment.substring(0, maxLength)).append('\r');
100             // parts 2..n-1 of the long segment
101             int startPos;
102             for (startPos = maxLength; startPos + restLength <= segment.length(); startPos += restLength) {
103                 sb.append(prefix)
104                   .append(segment.substring(startPos, startPos + restLength))
105                   .append('\r');
106             }
107             // part n of the long segment
108             if (startPos < segment.length()) {
109                 sb.append(prefix).append(segment.substring(startPos)).append('\r');
110             }
111         }
112         return sb.toString();
113     }
114 
115 
116     /**
117      * Appends a split segment to the given StringBuilder.
118      */
119     public static void appendSplitSegment(StringBuilder sb, List<String> fields, char fieldSeparator) {
120         for (String field : fields) {
121             sb.append(field).append(fieldSeparator);
122         }
123         for (int len = sb.length(); sb.charAt(--len) == fieldSeparator; ) {
124             sb.setLength(len);
125         }
126         sb.append('\r');
127     }
128 
129 
130     /**
131      * Appends segments from startIndex to endIndex-1 to the given StringBuilder.
132      */
133     public static void appendSegments(StringBuilder sb, List<String> segments, int startIndex, int endIndex) {
134         for (int i = startIndex; i < endIndex; ++i) {
135             sb.append(segments.get(i)).append('\r');
136         }
137     }
138 
139 
140     /**
141      * Joins segments from startIndex to endIndex-1.
142      */
143     public static CharSequence joinSegments(List<String> segments, int startIndex, int endIndex) {
144         StringBuilder sb = new StringBuilder();
145         appendSegments(sb, segments, startIndex, endIndex);
146         return sb;
147     }
148 
149 
150     /**
151      * Creates a single key string from the given key pieces.
152      */
153     public static String keyString(String... pieces) {
154         StringBuilder sb = new StringBuilder(pieces[0]);
155         for (int i = 1; i < pieces.length; ++i) {
156             sb.append('\0').append(pieces[i]);
157         }
158         return sb.toString();
159     }
160 
161 
162     /**
163      * Returns an unique value which can be used, for example, as an HL7v2 message ID.
164      */
165     public static String uniqueId() {
166         return UUID.randomUUID().toString();
167     }
168 }