1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package ca.uhn.hl7v2.model;
29
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37
38 import ca.uhn.hl7v2.HL7Exception;
39 import ca.uhn.hl7v2.Location;
40 import ca.uhn.hl7v2.VersionLogger;
41 import ca.uhn.hl7v2.parser.EncodingCharacters;
42 import ca.uhn.hl7v2.parser.ModelClassFactory;
43 import ca.uhn.hl7v2.parser.PipeParser;
44 import ca.uhn.hl7v2.util.ReflectionUtil;
45
46
47
48
49
50
51
52
53
54 public abstract class AbstractGroup extends AbstractStructure implements Group {
55
56 private static final int PS_INDENT = 3;
57
58 private static final long serialVersionUID = 1772720246448224363L;
59
60 private List<String> names;
61 private Map<String, List<Structure>> structures;
62 private Map<String, Boolean> required;
63 private Map<String, Boolean> repeating;
64 private Set<String> choiceElements;
65 private Map<String, Class<? extends Structure>> classes;
66
67 private Set<String> nonStandardNames;
68 private final ModelClassFactory myFactory;
69
70 static {
71 VersionLogger.init();
72 }
73
74
75
76
77
78
79
80 protected AbstractGroup(Group parent, ModelClassFactory factory) {
81 super(parent);
82 this.myFactory = factory;
83 init();
84 }
85
86 private void init() {
87 names = new ArrayList<>();
88 structures = new HashMap<>();
89 required = new HashMap<>();
90 repeating = new HashMap<>();
91 classes = new HashMap<>();
92 choiceElements = new HashSet<>();
93 }
94
95
96
97
98
99
100
101 public Structure get(String name) throws HL7Exception {
102 return get(name, 0);
103 }
104
105 protected <T extends Structure> T getTyped(String name, Class<T> type) {
106 try {
107 @SuppressWarnings("unchecked")
108 T ret = (T) get(name);
109 return ret;
110 } catch (HL7Exception e) {
111 log.error("Unexpected error accessing data - this is probably a bug in the source code generator.", e);
112 throw new RuntimeException(e);
113 }
114 }
115
116
117
118
119
120
121
122
123
124 public Structure get(String name, int rep) throws HL7Exception {
125 List<Structure> list = structures.get(name);
126 if (list == null)
127 throw new HL7Exception(name + " does not exist in the group " + this.getClass().getName());
128
129 Structure ret;
130 if (rep < list.size()) {
131
132 ret = list.get(rep);
133 } else if (rep == list.size()) {
134
135 Boolean repeats = this.repeating.get(name);
136 if (!repeats && list.size() > 0)
137 throw new HL7Exception("Can't create repetition #" + rep + " of Structure " + name
138 + " - this Structure is non-repeating so only rep 0 may be retrieved");
139
140
141 Class<? extends Structure> c = classes.get(name);
142 ret = tryToInstantiateStructure(c, name);
143 list.add(ret);
144 } else {
145 StringBuilder b = new StringBuilder();
146 b.append("Can't return repetition #");
147 b.append(rep);
148 b.append(" of ");
149 b.append(name);
150 b.append(" - there are currently ");
151 if (list.size() == 0) {
152 b.append("no");
153 } else {
154 b.append("only ");
155 b.append(list.size());
156 }
157 b.append(" repetitions ");
158 b.append("so rep# must be ");
159 if (list.size() == 0) {
160 b.append("0");
161 } else {
162 b.append("between 0 and ");
163 b.append(list.size());
164 }
165 throw new HL7Exception(b.toString());
166 }
167 return ret;
168 }
169
170
171
172
173 public boolean isEmpty() throws HL7Exception {
174 for (String name : getNames()) {
175 if (!get(name).isEmpty())
176 return false;
177 }
178 return true;
179 }
180
181 protected <T extends Structure> T getTyped(String name, int rep, Class<T> type) {
182 try {
183 @SuppressWarnings("unchecked")
184 T ret = (T) get(name, rep);
185 return ret;
186 } catch (HL7Exception e) {
187 List<Structure> list = structures.get(name);
188 if (list != null && list.size() < rep) {
189
190 } else {
191 log.error("Unexpected error accessing data - this is probably a bug in the source code generator.", e);
192 }
193 throw new RuntimeException(e);
194 }
195 }
196
197 protected int getReps(String name) {
198 try {
199 return getAll(name).length;
200 } catch (HL7Exception e) {
201 String message = "Unexpected error accessing data - this is probably a bug in the source code generator.";
202 log.error(message, e);
203 throw new RuntimeException(message);
204 }
205 }
206
207
208
209
210
211
212
213
214 public String addNonstandardSegment(String name) throws HL7Exception {
215 String version = this.getMessage().getVersion();
216 if (version == null)
217 throw new HL7Exception("Need message version to add segment by name; message.getVersion() returns null");
218 Class<? extends Segment> c = myFactory.getSegmentClass(name, version);
219 if (c == null)
220 c = GenericSegment.class;
221
222 int index = this.getNames().length;
223
224 tryToInstantiateStructure(c, name);
225
226 String newName = insert(c, false, true, index, name);
227 if (this.nonStandardNames == null) {
228 this.nonStandardNames = new HashSet<>();
229 }
230 this.nonStandardNames.add(newName);
231
232 return newName;
233 }
234
235 public String addNonstandardSegment(String theName, int theIndex) throws HL7Exception {
236 if (this instanceof Message && theIndex == 0) {
237 throw new HL7Exception("Can not add nonstandard segment \"" + theName + "\" to start of message.");
238 }
239
240 String version = this.getMessage().getVersion();
241 if (version == null)
242 throw new HL7Exception("Need message version to add segment by name; message.getVersion() returns null");
243 Class<? extends Segment> c = myFactory.getSegmentClass(theName, version);
244
245 if (c == null) {
246 c = GenericSegment.class;
247 }
248
249 tryToInstantiateStructure(c, theName);
250
251 String newName = insert(c, false, true, theIndex, theName);
252 if (this.nonStandardNames == null) {
253 this.nonStandardNames = new HashSet<>();
254 }
255 this.nonStandardNames.add(newName);
256
257 return newName;
258 }
259
260
261
262
263
264
265
266 public Set<String> getNonStandardNames() {
267 if (nonStandardNames == null) {
268 return Collections.emptySet();
269 }
270 return Collections.unmodifiableSet(nonStandardNames);
271 }
272
273
274
275
276
277 public String[] getNames() {
278 String[] retVal = new String[this.names.size()];
279 for (int i = 0; i < this.names.size(); i++) {
280 retVal[i] = this.names.get(i);
281 }
282 return retVal;
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296
297 protected String add(Class<? extends Structure> c, boolean required, boolean repeating) throws HL7Exception {
298 return add(c, required, repeating, false);
299 }
300
301
302
303
304
305
306
307
308
309
310
311
312
313 protected String add(Class<? extends Structure> c, boolean required, boolean repeating, boolean choiceElement) throws HL7Exception {
314 String name = getName(c);
315 return insert(c, required, repeating, choiceElement, this.names.size(), name);
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329
330 protected String add(Class<? extends Structure> c, boolean required, boolean repeating, int index)
331 throws HL7Exception {
332 String name = getName(c);
333 return insert(c, required, repeating, index, name);
334 }
335
336
337
338
339 private boolean nameExists(String name) {
340 return this.classes.get(name) != null;
341 }
342
343
344
345
346
347
348
349 protected Structure tryToInstantiateStructure(Class<? extends Structure> c, String name) throws HL7Exception {
350 if (GenericSegment.class.isAssignableFrom(c)) {
351 String genericName = name;
352 if (genericName.length() > 3) {
353 genericName = genericName.substring(0, 3);
354 }
355 return new GenericSegment(this, genericName);
356 }
357 if (GenericGroup.class.isAssignableFrom(c)) {
358 return new GenericGroup(this, name, myFactory);
359 }
360 try {
361 return ReflectionUtil.instantiateStructure(c, this, myFactory);
362 } catch (Exception e) {
363 return ReflectionUtil.instantiate(c);
364 }
365
366 }
367
368
369
370
371 public boolean isChoiceElement(String theName) throws HL7Exception {
372 return choiceElements.contains(theName);
373 }
374
375
376
377
378 public boolean isGroup(String name) throws HL7Exception {
379 Class<? extends Structure> clazz = classes.get(name);
380 if (clazz == null)
381 throw new HL7Exception("The structure " + name + " does not exist in the group "
382 + this.getClass().getName());
383 return Group.class.isAssignableFrom(clazz);
384 }
385
386
387
388
389 public boolean isRequired(String name) throws HL7Exception {
390 Boolean req = required.get(name);
391 if (req == null)
392 throw new HL7Exception("The structure " + name + " does not exist in the group "
393 + this.getClass().getName());
394 return req;
395 }
396
397
398
399
400 public boolean isRepeating(String name) throws HL7Exception {
401 Boolean rep = repeating.get(name);
402 if (rep == null)
403 throw new HL7Exception("The structure " + name + " does not exist in the group "
404 + this.getClass().getName());
405 return rep;
406 }
407
408
409
410
411
412
413
414
415 public int currentReps(String name) throws HL7Exception {
416 List<Structure> list = structures.get(name);
417 if (list == null)
418 throw new HL7Exception("The structure " + name + " does not exist in the group "
419 + this.getClass().getName());
420 return list.size();
421 }
422
423
424
425
426
427
428
429
430
431 public Structure[] getAll(String name) throws HL7Exception {
432 List<Structure> list = structures.get(name);
433 if (list == null) {
434 throw new HL7Exception("The structure " + name + " does not exist in the group "
435 + this.getClass().getName());
436 }
437 return list.toArray(new Structure[0]);
438 }
439
440
441
442
443
444
445 @SuppressWarnings("unchecked")
446 protected <T extends Structure> List<T> getAllAsList(String name, Class<T> theType) throws HL7Exception {
447 Class<? extends Structure> clazz = classes.get(name);
448
449 if (!theType.equals(clazz)) {
450 throw new HL7Exception("Structure with name \"" + name + "\" has type " + clazz.getName()
451 + " but should be " + theType);
452 }
453 List<T> retVal = new ArrayList<>();
454 for (Structure next : structures.get(name)) {
455 retVal.add((T) next);
456 }
457 return Collections.unmodifiableList(retVal);
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471 public Structure removeRepetition(String name, int index) throws HL7Exception {
472 List<Structure> list = structures.get(name);
473 if (list == null) {
474 throw new HL7Exception("The structure " + name + " does not exist in the group "
475 + this.getClass().getName());
476 }
477 if (list.size() == 0) {
478 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " has no repetitions");
479 }
480 if (list.size() <= index) {
481 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " must be between 0 and "
482 + (list.size() - 1));
483 }
484
485 return list.remove(index);
486 }
487
488
489
490
491
492
493
494
495
496 protected void insertRepetition(String name, Structure structure, int index) throws HL7Exception {
497 if (structure == null) {
498 throw new NullPointerException("Structure may not be null");
499 }
500
501 if (structure.getMessage() != this.getMessage()) {
502 throw new HL7Exception("Structure does not belong to this message");
503 }
504
505 List<Structure> list = structures.get(name);
506
507 if (list == null) {
508 throw new HL7Exception("The structure " + name + " does not exist in the group "
509 + this.getClass().getName());
510 }
511 if (list.size() < index) {
512 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " must be between 0 and "
513 + (list.size()));
514 }
515
516 list.add(index, structure);
517 }
518
519
520
521
522
523
524
525
526
527
528
529
530 public Structure insertRepetition(String name, int index) throws HL7Exception {
531 if (name == null || name.length() == 0) {
532 throw new NullPointerException("Name may not be null/empty");
533 }
534
535 Class<? extends Structure> structureClass = this.classes.get(name);
536 if (structureClass == null) {
537 throw new HL7Exception("Group " + this.getClass().getName() + " has no structure named " + name
538 + ": Valid names: " + this.classes.keySet());
539 }
540
541 Structure rep = tryToInstantiateStructure(structureClass, name);
542 insertRepetition(name, rep, index);
543
544 return rep;
545 }
546
547
548
549
550
551
552
553
554
555 public int getFieldNumForName(String name) throws HL7Exception {
556 int retVal = names.indexOf(name);
557 if (retVal == -1) {
558 throw new HL7Exception("Unknown name: " + name);
559 }
560 return retVal + 1;
561 }
562
563
564
565
566 public Class<? extends Structure> getClass(String name) {
567 return classes.get(name);
568 }
569
570
571
572
573
574
575 public String getName() {
576 return getName(getClass());
577 }
578
579
580 private String getName(Class<? extends Structure> c) {
581 String name = c.getSimpleName();
582 if (Group.class.isAssignableFrom(c) && !Message.class.isAssignableFrom(c)) {
583 name = getGroupName(name);
584 }
585 return name;
586 }
587
588
589
590
591
592
593
594
595
596 private String getGroupName(String name) {
597 Class<?> messageClass = getMessage().getClass();
598 while (Message.class.isAssignableFrom(messageClass)) {
599 @SuppressWarnings("unchecked")
600
601
602 String messageName = getName((Class<? extends Message>)messageClass);
603 if (name.startsWith(messageName) && name.length() > messageName.length()) {
604 return name.substring(messageName.length() + 1);
605 }
606 messageClass = messageClass.getSuperclass();
607 }
608 return name;
609 }
610
611
612
613
614
615
616
617
618
619 protected String insert(Class<? extends Structure> c, boolean required, boolean repeating, int index, String name)
620 throws HL7Exception {
621 return insert(c, required, repeating, false, index, name);
622 }
623
624 protected String insert(Class<? extends Structure> c, boolean required, boolean repeating, boolean choiceElement,
625 int index, String name) throws HL7Exception {
626
627
628
629
630 if (nameExists(name)) {
631 int version = 2;
632 String newName = name;
633 while (nameExists(newName)) {
634 newName = name + version++;
635 }
636 name = newName;
637 }
638
639 if (index > this.names.size()) {
640 throw new HL7Exception("Invalid index " + index + " - Should be <= " + this.names.size());
641 }
642
643 this.names.add(index, name);
644 this.required.put(name, required);
645 this.repeating.put(name, repeating);
646 this.classes.put(name, c);
647 this.structures.put(name, new ArrayList<>());
648
649 if (choiceElement) {
650 this.choiceElements.add(name);
651 }
652
653 return name;
654 }
655
656
657
658
659 public void clear() {
660 for (List<Structure> next : structures.values()) {
661 if (next != null) {
662 next.clear();
663 }
664 }
665 }
666
667
668
669
670
671
672 public final ModelClassFactory getModelClassFactory() {
673 return myFactory;
674 }
675
676
677
678
679
680
681
682
683
684
685 public boolean accept(MessageVisitor visitor, Location location) throws HL7Exception {
686 if (visitor.start(this, location)) {
687 visitNestedStructures(visitor, location);
688 }
689 return visitor.end(this, location);
690 }
691
692 public Location/../ca/uhn/hl7v2/Location.html#Location">Location provideLocation(Location location, int index, int repetition) {
693 return new Location(location).pushGroup(getName(), repetition);
694 }
695
696 protected void visitNestedStructures(MessageVisitor visitor, Location location) throws HL7Exception {
697 for (String name : getNames()) {
698 Structure[] structures = getAll(name);
699 for (int j=0; j < structures.length; j++) {
700 int rep = isRepeating(name) ? j : -1;
701 Location nextLocation = structures[j].provideLocation(location, -1, rep);
702 if (!structures[j].accept(visitor, nextLocation)) break;
703 }
704 }
705 }
706
707
708
709
710
711
712
713
714
715
716
717
718 void appendStructureDescription(StringBuilder theStringBuilder, int theIndent, boolean theOptional,
719 boolean theRepeating, boolean theAddStartName, boolean theAddEndName, boolean thePrintEmpty) throws HL7Exception {
720 String lineSeparator = System.getProperty("line.separator");
721
722 if (theAddStartName) {
723 indent(theStringBuilder, theIndent);
724 theStringBuilder.append(getName()).append(" (start)").append(lineSeparator);
725 }
726
727 if (theOptional || theRepeating) {
728 indent(theStringBuilder, theIndent);
729 if (theOptional) {
730 theStringBuilder.append("[");
731 }
732 if (theRepeating) {
733 theStringBuilder.append("{");
734 }
735 theStringBuilder.append(lineSeparator);
736 }
737
738 boolean inChoice = false;
739
740 for (String nextName : getNames()) {
741
742 if (!thePrintEmpty) {
743 boolean hasContent = false;
744 Structure[] allReps = getAll(nextName);
745 for (Structure structure : allReps) {
746 if (!structure.isEmpty()) {
747 hasContent = true;
748 break;
749 }
750 }
751
752 if (!hasContent) {
753 continue;
754 }
755 }
756
757 Class<? extends Structure> nextClass = classes.get(nextName);
758
759 boolean nextOptional = !isRequired(nextName);
760 boolean nextRepeating = isRepeating(nextName);
761 boolean nextChoice = isChoiceElement(nextName);
762
763 if (nextChoice && !inChoice) {
764 theIndent += PS_INDENT;
765 indent(theStringBuilder, theIndent);
766 theStringBuilder.append("<");
767 theStringBuilder.append(lineSeparator);
768 inChoice = true;
769 } else if (!nextChoice && inChoice) {
770 indent(theStringBuilder, theIndent);
771 theStringBuilder.append(">");
772 theStringBuilder.append(lineSeparator);
773 inChoice = false;
774 theIndent -= PS_INDENT;
775 } else if (nextChoice) {
776 indent(theStringBuilder, theIndent);
777 theStringBuilder.append("|");
778 theStringBuilder.append(lineSeparator);
779 }
780
781 if (AbstractGroup.class.isAssignableFrom(nextClass)) {
782
783 Structure[] nextChildren = getAll(nextName);
784 for (int i = 0; i < nextChildren.length; i++) {
785
786 Structure structure = nextChildren[i];
787 boolean addStartName = (i == 0);
788 boolean addEndName = (i == (nextChildren.length - 1));
789 ((AbstractGroup) structure).appendStructureDescription(theStringBuilder, theIndent + PS_INDENT,
790 nextOptional, nextRepeating, addStartName, addEndName, thePrintEmpty);
791
792 }
793
794 if (nextChildren.length == 0) {
795 Structure structure = tryToInstantiateStructure(nextClass, nextName);
796 ((AbstractGroup) structure).appendStructureDescription(theStringBuilder, theIndent + PS_INDENT,
797 nextOptional, nextRepeating, true, true, thePrintEmpty);
798 }
799
800 } else if (Segment.class.isAssignableFrom(nextClass)) {
801
802 int currentIndent = theStringBuilder.length();
803
804 StringBuilder structurePrefix = new StringBuilder();
805 indent(structurePrefix, theIndent + PS_INDENT);
806 if (nextOptional) {
807 structurePrefix.append("[ ");
808 }
809 if (nextRepeating) {
810 structurePrefix.append("{ ");
811 }
812 structurePrefix.append(nextName);
813 if (nextRepeating) {
814 structurePrefix.append(" }");
815 }
816 if (nextOptional) {
817 structurePrefix.append(" ]");
818 }
819
820 if (this.nonStandardNames != null && this.nonStandardNames.contains(nextName)) {
821 structurePrefix.append(" (non-standard)");
822 }
823 structurePrefix.append(" - ");
824
825 currentIndent = theStringBuilder.length() - currentIndent;
826 List<Structure> nextStructureList = structures.get(nextName);
827 theStringBuilder.append(structurePrefix);
828 if (nextStructureList == null || nextStructureList.isEmpty()) {
829 theStringBuilder.append("Not populated");
830 theStringBuilder.append(lineSeparator);
831 } else {
832 for (int i = 0; i < nextStructureList.size(); i++) {
833 if (i > 0) {
834 indent(theStringBuilder, currentIndent + structurePrefix.length());
835 }
836 Segment../../ca/uhn/hl7v2/model/Segment.html#Segment">Segment nextSegment = (Segment) nextStructureList.get(i);
837 theStringBuilder.append(new PipeParser().doEncode(nextSegment,
838 EncodingCharacters.getInstance(getMessage())));
839 theStringBuilder.append(lineSeparator);
840
841 }
842 }
843
844 }
845 }
846
847 if (inChoice) {
848 indent(theStringBuilder, theIndent);
849 theStringBuilder.append(">");
850 theStringBuilder.append(lineSeparator);
851 theIndent -= PS_INDENT;
852 }
853
854 if (theOptional || theRepeating) {
855 indent(theStringBuilder, theIndent);
856 if (theRepeating) {
857 theStringBuilder.append("}");
858 }
859 if (theOptional) {
860 theStringBuilder.append("]");
861 }
862 theStringBuilder.append(lineSeparator);
863 }
864
865 if (theAddEndName) {
866 indent(theStringBuilder, theIndent);
867 theStringBuilder.append(getName()).append(" (end)").append(lineSeparator);
868 }
869 }
870
871 private void indent(StringBuilder theStringBuilder, int theIndent) {
872 for (int i = 0; i < theIndent; i++) {
873 theStringBuilder.append(' ');
874 }
875 }
876 }