1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package ca.uhn.hl7v2.testpanel.util.compare;
23
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Set;
29
30 import org.apache.commons.lang.StringUtils;
31
32 import ca.uhn.hl7v2.HL7Exception;
33 import ca.uhn.hl7v2.model.AbstractGroup;
34 import ca.uhn.hl7v2.model.Composite;
35 import ca.uhn.hl7v2.model.Group;
36 import ca.uhn.hl7v2.model.Message;
37 import ca.uhn.hl7v2.model.Primitive;
38 import ca.uhn.hl7v2.model.Segment;
39 import ca.uhn.hl7v2.model.Structure;
40 import ca.uhn.hl7v2.model.Type;
41 import ca.uhn.hl7v2.model.Varies;
42 import ca.uhn.hl7v2.parser.EncodingCharacters;
43 import ca.uhn.hl7v2.parser.PipeParser;
44 import ca.uhn.hl7v2.testpanel.ex.UnexpectedTestFailureException;
45 import ca.uhn.hl7v2.testpanel.util.Pair;
46 import ca.uhn.hl7v2.validation.impl.ValidationContextImpl;
47
48 public class Hl7V2MessageCompare {
49
50
51
52 private Message myActualMessage;
53 private GroupComparison myComparison;
54
55 private EncodingCharactersdingCharacters">EncodingCharacters myEncodingCharacters = new EncodingCharacters('|', null);
56
57 private PipeParser myEncodingParser;
58 private Message myExpectedMessage;
59 private Set<String> myFieldsToIgnore;
60 private String myExpectedDesc = BulkHl7V2Comparison.EXPECTED_DESC;
61 private String myActualDesc = BulkHl7V2Comparison.ACTUAL_DESC;
62
63
64
65
66 public Hl7V2MessageCompare() {
67 myEncodingParser = new PipeParser();
68 myEncodingParser.setValidationContext(new ValidationContextImpl());
69 }
70
71
72
73
74 public Hl7V2MessageCompare(PipeParser theParser) {
75 myEncodingParser = theParser;
76 }
77
78
79
80
81 private void addRemainingStructures(Group theStructure, int theStartingNameIndex, int theAfterEndingIndex, List<StructureComparison> theStructureComparisons, boolean theIsMessage1) throws HL7Exception {
82 String[] names = theStructure.getNames();
83
84 for (int i = theStartingNameIndex; i < theAfterEndingIndex; i++) {
85 Structure[] reps = theStructure.getAll(names[i]);
86
87 for (Structure structure : reps) {
88 if (structure instanceof Group) {
89 theStructureComparisons.add(new ExtraGroup((Group) structure, theIsMessage1));
90 } else {
91 if (theIsMessage1) {
92 theStructureComparisons.add(new SegmentComparison(structure.getName(), (Segment) structure, null));
93 } else {
94 theStructureComparisons.add(new SegmentComparison(structure.getName(), null, (Segment) structure));
95 }
96 }
97 }
98 }
99 }
100
101
102
103
104 private void clearComponentIndex(Type[] theFieldReps, int theComponentIndex) throws HL7Exception {
105
106 for (Type type : theFieldReps) {
107
108 int extraComponentIndex = -1;
109
110 if (type instanceof Primitive) {
111 Primitive p = (Primitive) type;
112 if (theComponentIndex < 2) {
113 p.setValue("");
114 } else {
115 extraComponentIndex = theComponentIndex - 1;
116 }
117 } else {
118 Composite c = (Composite) type;
119 if (theComponentIndex <= c.getComponents().length) {
120 c.getComponents()[theComponentIndex - 1].parse("");
121 } else {
122 extraComponentIndex = theComponentIndex - c.getComponents().length;
123 }
124 }
125
126 if (extraComponentIndex != -1) {
127 if (type.getExtraComponents().numComponents() >= (theComponentIndex)) {
128 type.getExtraComponents().getComponent(extraComponentIndex).parse("");
129 }
130 }
131
132 }
133
134 }
135
136
137
138
139 public void compare(Message../../../ca/uhn/hl7v2/model/Message.html#Message">Message theExpectMessage, Message theActualMessage) throws UnexpectedTestFailureException {
140 try {
141 myExpectedMessage = theExpectMessage;
142 myActualMessage = theActualMessage;
143
144 myExpectedMessage = myEncodingParser.parse(theExpectMessage.encode());
145 myActualMessage = myEncodingParser.parse(theActualMessage.encode());
146
147 stripEmptyStructures(theExpectMessage);
148 stripEmptyStructures(theActualMessage);
149
150 myComparison = compareGroups(theExpectMessage, theActualMessage);
151 } catch (HL7Exception ex) {
152 throw new UnexpectedTestFailureException(ex);
153 }
154 }
155
156 private FieldComparison compareFields(Segment./../../../../ca/uhn/hl7v2/model/Segment.html#Segment">Segment theSegment1, Segment theSegment2, int theI) throws HL7Exception {
157
158 String fieldDescriptor = theSegment1.getName() + "-" + (theI + 1);
159
160
161 boolean ignore = (myFieldsToIgnore != null) && (myFieldsToIgnore.contains(fieldDescriptor));
162
163 Type[] reps1 = theSegment1.getField(theI + 1);
164 Type[] reps2 = theSegment2.getField(theI + 1);
165
166
167 if (myFieldsToIgnore != null) {
168 for (String fieldsToIgnore : myFieldsToIgnore) {
169 if (fieldsToIgnore.length() > fieldDescriptor.length() + 1) {
170 if (fieldsToIgnore.startsWith(fieldDescriptor)) {
171 String componentIndexStr = fieldsToIgnore.substring(fieldDescriptor.length() + 1);
172 int componentIndex = Integer.parseInt(componentIndexStr);
173 clearComponentIndex(reps1, componentIndex);
174 clearComponentIndex(reps2, componentIndex);
175 }
176 }
177 }
178 }
179
180 int maxReps = (reps1.length > reps2.length) ? reps1.length : reps2.length;
181
182 List<Type> sameFields = new ArrayList<Type>();
183 List<Type> diffFields1 = new ArrayList<Type>();
184 List<Type> diffFields2 = new ArrayList<Type>();
185
186 if (!ignore) {
187 for (int i = 0; i < maxReps; i++) {
188 if (i >= reps1.length) {
189 if (reps2[i] == null || reps2[i].encode() == null || reps2[i].encode().isEmpty()) {
190 sameFields.add(reps2[i]);
191 diffFields1.add(null);
192 diffFields2.add(null);
193 } else {
194 sameFields.add(null);
195 diffFields1.add(null);
196 diffFields2.add(reps2[i]);
197 }
198 } else if (i >= reps2.length) {
199 Type type = reps1[i];
200 if (type == null || type.encode() == null || type.encode().isEmpty()) {
201 sameFields.add(reps1[i]);
202 diffFields1.add(null);
203 diffFields2.add(null);
204 } else {
205 sameFields.add(null);
206 diffFields1.add(reps1[i]);
207 diffFields2.add(null);
208 }
209 } else {
210 if (compareTypes(reps1[i], reps2[i])) {
211 sameFields.add(reps1[i]);
212 diffFields1.add(null);
213 diffFields2.add(null);
214 } else {
215 sameFields.add(null);
216 diffFields1.add(reps1[i]);
217 diffFields2.add(reps2[i]);
218 }
219 }
220 }
221 }
222
223 return new FieldComparison(theSegment1.getNames()[theI], sameFields, diffFields1, diffFields2);
224 }
225
226 private GroupComparison compareGroups(Group./../../../../ca/uhn/hl7v2/model/Group.html#Group">Group theStructure1, Group theStructure2) throws HL7Exception {
227 List<String> originalNames1 = Arrays.asList(theStructure1.getNames());
228 ArrayList<String> names1 = new ArrayList<String>(originalNames1);
229 for (Iterator<String> iter = names1.iterator(); iter.hasNext();) {
230 String nextName = iter.next();
231 if (theStructure1.getAll(nextName).length == 0) {
232 iter.remove();
233 }
234 }
235
236 List<String> originalNames2 = Arrays.asList(theStructure2.getNames());
237 ArrayList<String> names2 = new ArrayList<String>(originalNames2);
238 for (Iterator<String> iter = names2.iterator(); iter.hasNext();) {
239 String nextName = iter.next();
240 if (theStructure2.getAll(nextName).length == 0) {
241 iter.remove();
242 }
243 }
244
245 List<StructureComparison> structureComparisons = new ArrayList<StructureComparison>();
246
247 int nameIdx1 = 0;
248 int nameIdx2 = 0;
249
250 while ((nameIdx1 < names1.size()) || (nameIdx2 < names2.size())) {
251 Pair<Integer> nextSameIdx = findNextSameIndex(names1, names2, nameIdx1, nameIdx2);
252
253
254 if (nextSameIdx == null) {
255 addRemainingStructures(theStructure1, nameIdx1, names1.size(), structureComparisons, true);
256 addRemainingStructures(theStructure2, nameIdx2, names2.size(), structureComparisons, false);
257
258 break;
259 }
260
261 addRemainingStructures(theStructure1, nameIdx1, nextSameIdx.getValue1(), structureComparisons, true);
262 addRemainingStructures(theStructure2, nameIdx2, nextSameIdx.getValue2(), structureComparisons, false);
263
264 Structure[] children1 = theStructure1.getAll(names1.get(nextSameIdx.getValue1()));
265 Structure[] children2 = theStructure2.getAll(names2.get(nextSameIdx.getValue2()));
266 int lowerCommonIndex = (children1.length < children2.length) ? children1.length : children2.length;
267
268 for (int i = 0; i < lowerCommonIndex; i++) {
269 Structure child1 = children1[i];
270 Structure child2 = children2[i];
271
272 if (child1 instanceof Segment) {
273 structureComparisons.add(compareSegments((Segment./../../../../../ca/uhn/hl7v2/model/Segment.html#Segment">Segment) child1, (Segment) child2));
274 } else {
275 structureComparisons.add(compareGroups((Group"../../../../../../ca/uhn/hl7v2/model/Group.html#Group">Group) child1, (Group) child2));
276 }
277 }
278
279 for (int i = lowerCommonIndex; i < children1.length; i++) {
280 if (children1[i] instanceof Segment) {
281 structureComparisons.add(new SegmentComparison(children1[i].getName(), (Segment) children1[i], null));
282 } else {
283 structureComparisons.add(new GroupComparison((Group) children1[i], null));
284 }
285 }
286
287 for (int i = lowerCommonIndex; i < children2.length; i++) {
288 if (children2[i] instanceof Segment) {
289 structureComparisons.add(new SegmentComparison(children2[i].getName(), null, (Segment) children2[i]));
290 } else {
291 structureComparisons.add(new GroupComparison(null, (Group) children2[i]));
292 }
293 }
294
295 nameIdx1 = nextSameIdx.getValue1() + 1;
296 nameIdx2 = nextSameIdx.getValue2() + 1;
297 }
298
299 return new GroupComparison(structureComparisons);
300 }
301
302 private SegmentComparison compareSegments(Segment./../../../../ca/uhn/hl7v2/model/Segment.html#Segment">Segment theSegment1, Segment theSegment2) throws HL7Exception {
303 assert theSegment1.getName().equals(theSegment2.getName());
304
305 List<FieldComparison> fieldComparisons = new ArrayList<FieldComparison>();
306
307 for (int i = 0; i < theSegment1.numFields(); i++) {
308 FieldComparison nextFieldComparison = compareFields(theSegment1, theSegment2, i);
309 fieldComparisons.add(nextFieldComparison);
310 }
311
312 return new SegmentComparison(theSegment1.getName(), fieldComparisons);
313 }
314
315 private boolean compareTypes(Type="../../../../../../ca/uhn/hl7v2/model/Type.html#Type">Type theType1, Type theType2) {
316 if (theType1 instanceof Primitive/hl7v2/model/Primitive.html#Primitive">Primitive && theType2 instanceof Primitive) {
317 Primitive type1 = (Primitive) theType1;
318 Primitive type2 = (Primitive) theType2;
319
320 return StringUtils.equals(type1.getValue(), type2.getValue());
321 } else if (theType1 instanceof Variesuhn/hl7v2/model/Varies.html#Varies">Varies && theType2 instanceof Varies) {
322 Varies type1 = (Varies) theType1;
323 Varies type2 = (Varies) theType2;
324
325 return compareTypes(type1.getData(), type2.getData());
326 } else if (theType1 instanceof Composite/hl7v2/model/Composite.html#Composite">Composite && theType2 instanceof Composite) {
327 Composite type1 = (Composite) theType1;
328 Composite type2 = (Composite) theType2;
329 Type[] components1 = type1.getComponents();
330 Type[] components2 = type2.getComponents();
331
332 if (components1.length != components2.length) {
333 return false;
334 }
335
336 for (int i = 0; i < components1.length; i++) {
337 if (!compareTypes(components1[i], components2[i])) {
338 return false;
339 }
340 }
341
342 return true;
343 } else {
344 return false;
345 }
346 }
347
348
349
350
351 public String describeDifference() {
352 StringBuilder retVal = new StringBuilder();
353
354 for (SegmentComparison nextSegment : myComparison.flattenMessage()) {
355 if (nextSegment.getExpectSegment() != null) {
356 retVal.append(myExpectedDesc).append(": ").append(PipeParser.encode(nextSegment.getExpectSegment(), myEncodingCharacters)).append("\r\n");
357 }
358
359 if (nextSegment.getActualSegment() != null) {
360 retVal.append(myActualDesc).append(": ").append(PipeParser.encode(nextSegment.getActualSegment(), myEncodingCharacters)).append("\r\n");
361 }
362
363 if (!nextSegment.isSame() && (nextSegment.getFieldComparisons() != null)) {
364 int fieldIndex = 0;
365
366 for (FieldComparison next : nextSegment.getFieldComparisons()) {
367 fieldIndex++;
368
369 for (int rep = 1; rep <= next.getDiffFieldsActual().size(); rep++) {
370 if (next.getSameFields().get(rep - 1) == null) {
371 retVal.append(nextSegment.getName());
372 retVal.append("-");
373 retVal.append(fieldIndex);
374 retVal.append("(");
375 retVal.append(rep);
376 retVal.append(") - ");
377 retVal.append(next.getFieldName());
378 retVal.append(":\r\n");
379
380 Type expectedType = next.getDiffFieldsExpected().get(rep - 1);
381 retVal.append(" ").append(myExpectedDesc).append(": ").append(encode(expectedType)).append("\r\n");
382
383 Type actualType = next.getDiffFieldsActual().get(rep - 1);
384 retVal.append(" ").append(myActualDesc).append(": ").append(encode(actualType));
385 retVal.append("\r\n");
386 }
387 }
388 }
389 }
390 }
391
392 return retVal.toString();
393 }
394
395 private String encode(Type theType) {
396 if (theType == null) {
397 return "";
398 }
399
400 return PipeParser.encode(theType, myEncodingCharacters);
401 }
402
403
404
405
406 public Message getActualMessage() {
407 return myActualMessage;
408 }
409
410
411
412
413 public Message getExpectedMessage() {
414 return myExpectedMessage;
415 }
416
417 public GroupComparison getMessageComparison() {
418 return myComparison;
419 }
420
421 private boolean hasData(Structure theStructure) throws HL7Exception {
422 if (theStructure instanceof Group) {
423 Group g = (Group) theStructure;
424 for (int nameIndex = 0; nameIndex < g.getNames().length; nameIndex++) {
425 String nextName = g.getNames()[nameIndex];
426 Structure[] nextReps = g.getAll(nextName);
427 for (int repIndex = 0; repIndex < nextReps.length; repIndex++) {
428 Structure nextRep = nextReps[repIndex];
429 if (hasData(nextRep)) {
430 return true;
431 }
432 }
433 }
434 } else {
435 Segment s = (Segment) theStructure;
436 for (int nameIndex = 0; nameIndex < s.getNames().length; nameIndex++) {
437 Type[] nextReps = s.getField(nameIndex + 1);
438 for (int repIndex = 0; repIndex < nextReps.length; repIndex++) {
439 Type nextRep = nextReps[repIndex];
440 if (StringUtils.isNotEmpty(nextRep.encode())) {
441 return true;
442 }
443 }
444 }
445 }
446
447 return false;
448 }
449
450
451
452
453 public boolean isSame() {
454 return myComparison.isSame();
455 }
456
457 public void setFieldsToIgnore(Set<String> theFieldsToIgnore) {
458 myFieldsToIgnore = theFieldsToIgnore;
459 }
460
461 private void stripEmptyStructures(Group theMessage) throws HL7Exception {
462 for (String nextName : theMessage.getNames()) {
463 for (int i = 0; i < theMessage.getAll(nextName).length; i++) {
464 Structure structure = theMessage.get(nextName, i);
465 if (structure instanceof Group) {
466 stripEmptyStructures((Group) structure);
467 }
468
469 if (!hasData(structure) && !theMessage.isRequired(nextName)) {
470 ((AbstractGroup) theMessage).removeRepetition(nextName, i);
471 }
472 }
473 }
474 }
475
476 public static Pair<Integer> findNextSameIndex(ArrayList<String> theNames1, ArrayList<String> theNames2, int theStartingIndex1, int theStartingIndex2) {
477 Pair<Integer> found1 = null;
478 BOTH: for (int i1 = theStartingIndex1; i1 < theNames1.size(); i1++) {
479 for (int i2 = theStartingIndex2; i2 < theNames2.size(); i2++) {
480 if (nameIsEqual(theNames1, theNames2, i1, i2)) {
481 found1 = new Pair<Integer>(i1, i2);
482
483 break BOTH;
484 }
485 }
486 }
487
488 Pair<Integer> found2 = null;
489 BOTH: for (int i2 = theStartingIndex2; i2 < theNames2.size(); i2++) {
490 for (int i1 = theStartingIndex1; i1 < theNames1.size(); i1++) {
491 if (nameIsEqual(theNames1, theNames2, i1, i2)) {
492 found2 = new Pair<Integer>(i1, i2);
493
494 break BOTH;
495 }
496 }
497 }
498
499 if (found1 == null) {
500 return found2;
501 } else if (found2 == null) {
502 return found1;
503 } else if (found1.getValue1() < found2.getValue1()) {
504 return found1;
505 } else {
506 return found2;
507 }
508 }
509
510 private static boolean nameIsEqual(ArrayList<String> theNames1, ArrayList<String> theNames2, int i1, int i2) {
511 String name1 = theNames1.get(i1);
512 if (!name1.contains("_") && name1.length() > 3) {
513 name1 = name1.substring(0, 3);
514 }
515
516 String name2 = theNames2.get(i2);
517 if (!name2.contains("_") && name2.length() > 3) {
518 name2 = name2.substring(0, 3);
519 }
520
521 return StringUtils.equals(name1, name2);
522 }
523
524
525
526
527 public void setExpectedAndActualDescription(String theExpectedDesc, String theActualDesc) {
528 myExpectedDesc = theExpectedDesc;
529 myActualDesc = theActualDesc;
530 }
531
532 }