1 package ca.uhn.hl7v2.parser;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.List;
6 import java.util.NoSuchElementException;
7
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10
11 import ca.uhn.hl7v2.HL7Exception;
12 import ca.uhn.hl7v2.model.Group;
13 import ca.uhn.hl7v2.model.Message;
14 import ca.uhn.hl7v2.model.Structure;
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 public class MessageIterator implements java.util.Iterator<Structure> {
32
33 private final Message myMessage;
34 private String myDirection;
35 private boolean myNextIsSet;
36 private final boolean myHandleUnexpectedSegments;
37 private List<Position> myCurrentDefinitionPath = new ArrayList<>();
38
39 private static final Logger log = LoggerFactory.getLogger(MessageIterator.class);
40
41
42
43
44
45
46
47
48
49
50 public MessageIterator(Message start, IStructureDefinition startDefinition, String direction, boolean handleUnexpectedSegments) {
51 this.myMessage = start;
52 this.myDirection = direction;
53 this.myHandleUnexpectedSegments = handleUnexpectedSegments;
54 this.myCurrentDefinitionPath.add(new Position(startDefinition, -1));
55 }
56
57 private Position getCurrentPosition() {
58 return getTail(myCurrentDefinitionPath);
59 }
60
61 private Position getTail(List<Position> theDefinitionPath) {
62 return theDefinitionPath.get(theDefinitionPath.size() - 1);
63 }
64
65 private List<Position> popUntilMatchFound(List<Position> theDefinitionPath) {
66 theDefinitionPath = new ArrayList<>(theDefinitionPath.subList(0, theDefinitionPath.size() - 1));
67
68 if (theDefinitionPath.size() == 0) {
69 return null;
70 }
71
72 Position newCurrentPosition = getTail(theDefinitionPath);
73 IStructureDefinition newCurrentStructureDefinition = newCurrentPosition.getStructureDefinition();
74
75 if (newCurrentStructureDefinition.getAllPossibleFirstChildren().contains(myDirection)) {
76 return theDefinitionPath;
77 }
78
79 if (newCurrentStructureDefinition.isFinalChildOfParent()) {
80 if (theDefinitionPath.size() > 1) {
81 return popUntilMatchFound(theDefinitionPath);
82 } else {
83 log.debug("Popped to root of message and did not find a match for {}", myDirection);
84 return null;
85 }
86 }
87
88 return theDefinitionPath;
89 }
90
91
92
93
94 @Override
95 public boolean hasNext() {
96
97 log.trace("hasNext() for direction {}", myDirection);
98 if (myDirection == null) {
99 throw new IllegalStateException("Direction not set");
100 }
101
102 while (!myNextIsSet) {
103
104 Position currentPosition = getCurrentPosition();
105
106 log.trace("hasNext() current position: {}", currentPosition);
107
108 IStructureDefinition structureDefinition = currentPosition.getStructureDefinition();
109
110 if (myMessage.getParser().getParserConfiguration().isNonGreedyMode()) {
111 IStructureDefinition nonGreedyPosition = couldBeNotGreedy();
112 if (nonGreedyPosition != null) {
113 log.info("Found non greedy parsing choice, moving to {}", nonGreedyPosition.getName());
114 while (getCurrentPosition().getStructureDefinition() != nonGreedyPosition) {
115 myCurrentDefinitionPath.remove(myCurrentDefinitionPath.size() - 1);
116 }
117 }
118 }
119
120 if (structureDefinition.isSegment() && structureDefinition.getName().startsWith(myDirection) && (structureDefinition.isRepeating() || currentPosition.getRepNumber() == -1)) {
121 myNextIsSet = true;
122 currentPosition.incrementRep();
123 } else if (structureDefinition.isSegment() && structureDefinition.getNextLeaf() == null
124 && !structureDefinition.getNamesOfAllPossibleFollowingLeaves().contains(myDirection)) {
125 if (!myHandleUnexpectedSegments) {
126 return false;
127 }
128 addNonStandardSegmentAtCurrentPosition();
129 } else if (structureDefinition.hasChildren() && structureDefinition.getAllPossibleFirstChildren().contains(myDirection) && (structureDefinition.isRepeating() || currentPosition.getRepNumber() == -1)) {
130 currentPosition.incrementRep();
131 myCurrentDefinitionPath.add(new Position(structureDefinition.getFirstChild(), -1));
132 } else if (!structureDefinition.hasChildren() && !structureDefinition.getNamesOfAllPossibleFollowingLeaves().contains(myDirection)) {
133 if (!myHandleUnexpectedSegments) {
134 return false;
135 }
136 addNonStandardSegmentAtCurrentPosition();
137
138
139
140
141
142 } else if (structureDefinition.isFinalChildOfParent()) {
143 List<Position> newDefinitionPath = popUntilMatchFound(myCurrentDefinitionPath);
144 if (newDefinitionPath != null) {
145
146 myCurrentDefinitionPath = newDefinitionPath;
147 } else {
148 if (!myHandleUnexpectedSegments) {
149 return false;
150 }
151 addNonStandardSegmentAtCurrentPosition();
152 }
153 } else {
154 currentPosition.setStructureDefinition(structureDefinition.getNextSibling());
155 currentPosition.resetRepNumber();
156 }
157
158 }
159
160 return true;
161 }
162
163
164
165
166 private IStructureDefinition couldBeNotGreedy() {
167 for (int i = myCurrentDefinitionPath.size() - 1; i >= 1; i--) {
168 Position position = myCurrentDefinitionPath.get(i);
169 IStructureDefinition curPos = position.getStructureDefinition();
170 if (curPos.getPosition() > 0) {
171 IStructureDefinition parent = curPos.getParent();
172 if (parent.isRepeating() && parent.getAllPossibleFirstChildren().contains(myDirection)) {
173 return parent;
174 }
175 }
176
177 }
178
179 return null;
180 }
181
182 private void addNonStandardSegmentAtCurrentPosition() throws Error {
183 log.debug("Creating non standard segment {} on group: {}",
184 myDirection, getCurrentPosition().getStructureDefinition().getParent().getName());
185
186 List<Position> parentDefinitionPath;
187 Group parentStructure;
188
189 switch (myMessage.getParser().getParserConfiguration().getUnexpectedSegmentBehaviour()) {
190 case ADD_INLINE:
191 default:
192 parentDefinitionPath = new ArrayList<>(myCurrentDefinitionPath.subList(0, myCurrentDefinitionPath.size() - 1));
193 parentStructure = (Group) navigateToStructure(parentDefinitionPath);
194 break;
195 case DROP_TO_ROOT:
196 parentDefinitionPath = new ArrayList<>(myCurrentDefinitionPath.subList(0, 1));
197 parentStructure = myMessage;
198 myCurrentDefinitionPath = myCurrentDefinitionPath.subList(0, 2);
199 break;
200 case THROW_HL7_EXCEPTION:
201 throw new Error(new HL7Exception("Found unknown segment: " + myDirection));
202 }
203
204
205
206 Position currentPosition = getCurrentPosition();
207 String nameAsItAppearsInParent = currentPosition.getStructureDefinition().getNameAsItAppearsInParent();
208
209 int index = Arrays.asList(parentStructure.getNames()).indexOf(nameAsItAppearsInParent) + 1;
210
211 String newSegmentName;
212
213
214
215 String[] currentNames = parentStructure.getNames();
216 if (index < currentNames.length && currentNames[index].startsWith(myDirection)) {
217 newSegmentName = currentNames[index];
218 } else {
219 try {
220 newSegmentName = parentStructure.addNonstandardSegment(myDirection, index);
221 } catch (HL7Exception e) {
222 throw new Error("Unable to add nonstandard segment " + myDirection + ": ", e);
223 }
224 }
225
226 IStructureDefinition previousSibling = getCurrentPosition().getStructureDefinition();
227 IStructureDefinition parentStructureDefinition = parentDefinitionPath.get(parentDefinitionPath.size() - 1).getStructureDefinition();
228 NonStandardStructureDefinitiontandardStructureDefinition">NonStandardStructureDefinition nextDefinition = new NonStandardStructureDefinition(parentStructureDefinition, previousSibling, newSegmentName, index);
229 myCurrentDefinitionPath = parentDefinitionPath;
230 myCurrentDefinitionPath.add(new Position(nextDefinition, 0));
231
232 myNextIsSet = true;
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 @Override
264 public Structure next() {
265 if (!hasNext()) {
266 throw new NoSuchElementException("No more nodes in message");
267 }
268
269 Structure currentStructure = navigateToStructure(myCurrentDefinitionPath);
270
271 clearNext();
272 return currentStructure;
273 }
274
275 private Structure navigateToStructure(List<Position> theDefinitionPath) throws Error {
276 Structure currentStructure = null;
277 for (Position next : theDefinitionPath) {
278 if (currentStructure == null) {
279 currentStructure = myMessage;
280 } else {
281 try {
282 IStructureDefinition structureDefinition = next.getStructureDefinition();
283 Group/uhn/hl7v2/model/Group.html#Group">Group currentStructureGroup = (Group) currentStructure;
284 String nextStructureName = structureDefinition.getNameAsItAppearsInParent();
285 currentStructure = currentStructureGroup.get(nextStructureName, next.getRepNumber());
286 } catch (HL7Exception e) {
287 throw new Error("Failed to retrieve structure: ", e);
288 }
289 }
290 }
291 return currentStructure;
292 }
293
294
295 @Override
296 public void remove() {
297 throw new UnsupportedOperationException("Can't remove a node from a message");
298 }
299
300 public String getDirection() {
301 return this.myDirection;
302 }
303
304 public void setDirection(String direction) {
305 clearNext();
306 this.myDirection = direction;
307 }
308
309 private void clearNext() {
310 myNextIsSet = false;
311 }
312
313
314
315
316 public static class Position {
317 private IStructureDefinition myStructureDefinition;
318 private int myRepNumber;
319
320 public IStructureDefinition getStructureDefinition() {
321 return myStructureDefinition;
322 }
323
324 public void resetRepNumber() {
325 myRepNumber = -1;
326 }
327
328 public void setStructureDefinition(IStructureDefinition theStructureDefinition) {
329 myStructureDefinition = theStructureDefinition;
330 }
331
332 public int getRepNumber() {
333 return myRepNumber;
334 }
335
336 public Position(IStructureDefinition theStructureDefinition, int theRepNumber) {
337 myStructureDefinition = theStructureDefinition;
338 myRepNumber = theRepNumber;
339 }
340
341 public void incrementRep() {
342 myRepNumber++;
343 }
344
345
346 public boolean equals(Object o) {
347 boolean equals = false;
348 if (o instanceof Position) {
349 Position p = (Position) o;
350 if (p.myStructureDefinition.equals(myStructureDefinition) && p.myRepNumber == myRepNumber)
351 equals = true;
352 }
353 return equals;
354 }
355
356
357 public int hashCode() {
358 return myStructureDefinition.hashCode() + myRepNumber;
359 }
360
361 public String toString() {
362 StringBuilder ret = new StringBuilder();
363
364 if (myStructureDefinition.getParent() != null) {
365 ret.append(myStructureDefinition.getParent().getName());
366 } else {
367 ret.append("Root");
368 }
369
370 ret.append(":");
371 ret.append(myStructureDefinition.getName());
372 ret.append("(");
373 ret.append(myRepNumber);
374 ret.append(")");
375 return ret.toString();
376 }
377 }
378
379
380
381
382 public int getNextIndexWithinParent() {
383 return getCurrentPosition().getStructureDefinition().getPosition();
384 }
385 }