1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.openehealth.ipf.commons.ihe.xds.core.metadata;
17
18 import lombok.AccessLevel;
19 import lombok.EqualsAndHashCode;
20 import lombok.Getter;
21 import org.apache.commons.beanutils.BeanUtils;
22 import org.apache.commons.lang3.StringUtils;
23
24 import java.io.Serializable;
25 import java.util.*;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 @EqualsAndHashCode(doNotUseGetters = true)
52 public class PatientInfo implements Serializable {
53 private static final long serialVersionUID = 7202574584233259959L;
54
55
56
57
58 private final Map<String, List<String>> stringFields = new HashMap<>();
59
60
61
62
63
64
65
66
67
68 private final Map<String, List<? extends Hl7v2Based>> pojoFields;
69
70 public PatientInfo() {
71 this.pojoFields = new HashMap<>();
72 this.pojoFields.put("PID-3", new ArrayList<Identifiable>());
73 this.pojoFields.put("PID-5", new ArrayList<Name>());
74 this.pojoFields.put("PID-7", null);
75 this.pojoFields.put("PID-8", null);
76 this.pojoFields.put("PID-11", new ArrayList<Address>());
77 }
78
79 private List<String> getStrings(String fieldId) {
80 return stringFields.computeIfAbsent(fieldId, dummy -> new ArrayList<>());
81 }
82
83
84
85
86
87 public Set<String> getAllFieldIds() {
88 Set<String> result = new HashSet<>(stringFields.keySet());
89 return result;
90 }
91
92
93
94
95
96
97 public Set<String> getCustomFieldIds() {
98 Set<String> set = getAllFieldIds();
99 set.removeAll(pojoFields.keySet());
100 return set;
101 }
102
103
104
105
106
107 public ListIterator<String> getHl7FieldIterator(String fieldId) {
108 ListIterator<String> stringsIterator = getStrings(fieldId).listIterator();
109 if (!pojoFields.containsKey(fieldId)) {
110 return stringsIterator;
111 }
112
113 List<? extends Hl7v2Based> xdsFields = pojoFields.get(fieldId);
114 ListIterator xdsIterator = (xdsFields != null) ? xdsFields.listIterator() : null;
115
116 return new SynchronizingListIterator<String, Hl7v2Based>(stringsIterator, xdsIterator) {
117 private void validateParameter(String s) {
118 if ((s != null) && s.contains("~")) {
119 throw new RuntimeException("Repetitions shall be handled by multiple calls to .add()/.set(), and not by the tilde in " + s);
120 }
121 }
122
123 @Override
124 public void set(String s) {
125 validateParameter(s);
126 getIterator().set(s);
127 switch (fieldId) {
128 case "PID-3":
129 getOtherIterator().set(Hl7v2Based.parse(s, Identifiable.class));
130 break;
131 case "PID-5":
132 getOtherIterator().set(Hl7v2Based.parse(s, XpnName.class));
133 break;
134 case "PID-7":
135 break;
136 case "PID-8":
137 break;
138 case "PID-11":
139 getOtherIterator().set(Hl7v2Based.parse(s, Address.class));
140 break;
141 default:
142 throw new RuntimeException("This line shall be not reachable, please report a bug");
143 }
144 }
145
146 @Override
147 public void add(String s) {
148 validateParameter(s);
149 getIterator().add(s);
150 switch (fieldId) {
151 case "PID-3":
152 getOtherIterator().add(Hl7v2Based.parse(s, Identifiable.class));
153 break;
154 case "PID-5":
155 getOtherIterator().add(Hl7v2Based.parse(s, XpnName.class));
156 break;
157 case "PID-7":
158 break;
159 case "PID-8":
160 break;
161 case "PID-11":
162 getOtherIterator().add(Hl7v2Based.parse(s, Address.class));
163 break;
164 default:
165 throw new RuntimeException("This line shall be not reachable, please report a bug");
166 }
167 }
168 };
169 }
170
171 private <T extends Hl7v2Based> ListIterator<T> getXdsFieldIterator(String fieldId) {
172 if (pojoFields.get(fieldId) == null) {
173 throw new IllegalArgumentException(fieldId + " is not a known repeatable SourcePatientInfo element");
174 }
175
176 ListIterator<T> xdsIterator = (ListIterator<T>) pojoFields.get(fieldId).listIterator();
177 ListIterator<String> stringsIterator = getStrings(fieldId).listIterator();
178
179 return new SynchronizingListIterator<T, String>(xdsIterator, stringsIterator) {
180 private T prepareValue(T xdsObject) {
181 if ("PID-5".equals(fieldId) && (xdsObject != null) && !(xdsObject instanceof XpnName)) {
182 Name xpnName = new XpnName();
183 try {
184 BeanUtils.copyProperties(xpnName, xdsObject);
185 return (T) xpnName;
186 } catch (Exception e) {
187 throw new RuntimeException("Could not copy properties", e);
188 }
189 }
190 return xdsObject;
191 }
192
193 @Override
194 public void set(T xdsObject) {
195 xdsObject = prepareValue(xdsObject);
196 getOtherIterator().set(StringUtils.trimToEmpty(Hl7v2Based.render(xdsObject)));
197 getIterator().set(xdsObject);
198 }
199
200 @Override
201 public void add(T xdsObject) {
202 xdsObject = prepareValue(xdsObject);
203 getOtherIterator().add(StringUtils.trimToEmpty(Hl7v2Based.render(xdsObject)));
204 getIterator().add(xdsObject);
205 }
206 };
207 }
208
209
210
211
212
213 public ListIterator<Identifiable> getIds() {
214 return getXdsFieldIterator("PID-3");
215 }
216
217
218
219
220
221 public ListIterator<Name> getNames() {
222 return getXdsFieldIterator("PID-5");
223 }
224
225 private String getFirstStringValue(String fieldName) {
226 List<String> list = stringFields.get(fieldName);
227 return ((list != null) && !list.isEmpty()) ? list.get(0) : null;
228 }
229
230
231
232
233 public Timestamp getDateOfBirth() {
234 String s = getFirstStringValue("PID-7");
235 if (s != null) {
236 int pos = s.indexOf('^');
237 return Timestamp.fromHL7((pos > 0) ? s.substring(0, pos) : s);
238 }
239 return null;
240 }
241
242
243
244
245 public void setDateOfBirth(Timestamp date) {
246 setDateOfBirthString(Timestamp.toHL7(date));
247 }
248
249
250
251
252 public void setDateOfBirth(String date) {
253 setDateOfBirthString(StringUtils.stripToNull(date));
254 }
255
256 private void setDateOfBirthString(String date) {
257 List<String> strings = getStrings("PID-7");
258 strings.clear();
259 if (date != null) {
260 strings.add(date);
261 }
262 }
263
264
265
266
267 public String getGender() {
268 return StringUtils.stripToNull(getFirstStringValue("PID-8"));
269 }
270
271
272
273
274 public void setGender(String gender) {
275 List<String> strings = getStrings("PID-8");
276 strings.clear();
277 String s = StringUtils.stripToNull(gender);
278 if (s != null) {
279 strings.add(s);
280 }
281 }
282
283
284
285
286
287 public ListIterator<Address> getAddresses() {
288 return getXdsFieldIterator("PID-11");
289 }
290
291 abstract private static class SynchronizingListIterator<T, OtherT> implements ListIterator<T> {
292 @Getter(AccessLevel.PROTECTED) private final ListIterator<T> iterator;
293 @Getter(AccessLevel.PROTECTED) private final ListIterator<OtherT> otherIterator;
294
295 SynchronizingListIterator(ListIterator<T> iterator, ListIterator<OtherT> otherIterator) {
296 this.iterator = iterator;
297 this.otherIterator = otherIterator;
298 }
299
300 @Override
301 public boolean hasNext() {
302 return iterator.hasNext();
303 }
304
305 @Override
306 public T next() {
307 if (otherIterator != null) {
308 otherIterator.next();
309 }
310 return iterator.next();
311 }
312
313 @Override
314 public boolean hasPrevious() {
315 return iterator.hasPrevious();
316 }
317
318 @Override
319 public T previous() {
320 if (otherIterator != null) {
321 otherIterator.previous();
322 }
323 return iterator.previous();
324 }
325
326 @Override
327 public int nextIndex() {
328 return iterator.nextIndex();
329 }
330
331 @Override
332 public int previousIndex() {
333 return iterator.previousIndex();
334 }
335
336 @Override
337 public void remove() {
338 if (otherIterator != null) {
339 otherIterator.remove();
340 }
341 iterator.remove();
342 }
343 }
344
345 public static class Hl7FieldIdComparator implements Comparator<String> {
346 public static final Pattern FIELD_ID_PATTERN = Pattern.compile("([A-Z][A-Z][A-Z0-9])-(\\d\\d?)");
347
348 @Override
349 public int compare(String o1, String o2) {
350 Matcher matcher1 = FIELD_ID_PATTERN.matcher(o1);
351 Matcher matcher2 = FIELD_ID_PATTERN.matcher(o2);
352 if (matcher1.matches() && matcher2.matches() && matcher1.group(1).equals(matcher2.group(1))) {
353 return Integer.parseInt(matcher1.group(2)) - Integer.parseInt(matcher2.group(2));
354 }
355 return o1.compareTo(o2);
356 }
357 }
358
359 @Override
360 public String toString() {
361 StringBuilder sb = new StringBuilder()
362 .append("PatientInfo(")
363 .append("ids=")
364 .append(pojoFields.get("PID-3"))
365 .append(", names=")
366 .append(pojoFields.get("PID-5"))
367 .append(", birthDate=")
368 .append(getDateOfBirth())
369 .append(", gender=")
370 .append(getGender())
371 .append(", addresses=")
372 .append(pojoFields.get("PID-11"));
373
374 getCustomFieldIds().stream().sorted(new Hl7FieldIdComparator()).forEach(fieldId -> {
375 List<String> values = stringFields.get(fieldId);
376 if (!values.isEmpty()) {
377 sb.append(", ").append(fieldId).append('=').append(values);
378 }
379 });
380
381 return sb.append(')').toString();
382 }
383 }