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  package ca.uhn.hl7v2.testpanel.model.msg;
27  
28  import static org.apache.commons.lang.StringUtils.*;
29  
30  import java.awt.EventQueue;
31  import java.beans.PropertyVetoException;
32  import java.io.IOException;
33  import java.io.StringReader;
34  import java.io.StringWriter;
35  import java.io.Writer;
36  import java.nio.charset.Charset;
37  import java.nio.charset.IllegalCharsetNameException;
38  import java.util.ArrayList;
39  import java.util.Collections;
40  import java.util.List;
41  import java.util.UUID;
42  import java.util.regex.Matcher;
43  import java.util.regex.Pattern;
44  
45  import jakarta.xml.bind.JAXB;
46  import jakarta.xml.bind.annotation.XmlAccessType;
47  import jakarta.xml.bind.annotation.XmlAccessorType;
48  import jakarta.xml.bind.annotation.XmlAttribute;
49  import jakarta.xml.bind.annotation.XmlElement;
50  import jakarta.xml.bind.annotation.XmlRootElement;
51  
52  import org.apache.commons.lang.StringUtils;
53  import org.apache.commons.lang.Validate;
54  import org.apache.commons.lang.builder.HashCodeBuilder;
55  import org.slf4j.Logger;
56  import org.slf4j.LoggerFactory;
57  
58  import ca.uhn.hl7v2.HL7Exception;
59  import ca.uhn.hl7v2.conf.ProfileException;
60  import ca.uhn.hl7v2.model.Message;
61  import ca.uhn.hl7v2.model.Segment;
62  import ca.uhn.hl7v2.parser.GenericParser;
63  import ca.uhn.hl7v2.testpanel.model.AbstractModelClass;
64  import ca.uhn.hl7v2.testpanel.model.UnknownMessage;
65  import ca.uhn.hl7v2.testpanel.model.conf.ProfileFileList;
66  import ca.uhn.hl7v2.testpanel.model.conf.ProfileGroup;
67  import ca.uhn.hl7v2.testpanel.ui.ShowEnum;
68  import ca.uhn.hl7v2.testpanel.util.ClassUtils;
69  import ca.uhn.hl7v2.testpanel.util.LineEndingsEnum;
70  import ca.uhn.hl7v2.testpanel.util.Range;
71  import ca.uhn.hl7v2.testpanel.util.SegmentAndComponentPath;
72  import ca.uhn.hl7v2.testpanel.xsd.Hl7V2EncodingTypeEnum;
73  import ca.uhn.hl7v2.validation.ValidationContext;
74  import ca.uhn.hl7v2.validation.impl.DefaultValidation;
75  import ca.uhn.hl7v2.validation.impl.ValidationContextImpl;
76  
77  public class Hl7V2MessageCollection extends AbstractModelClass {
78  	private static final Pattern FIRSTLINE_COMMENT_PATTERN = Pattern.compile("^\\#\\s*(\\S.*)");
79  	private static final Logger ourLog = LoggerFactory.getLogger(Hl7V2MessageCollection.class);
80  	public static final String PARSED_MESSAGES_PROPERTY = Hl7V2MessageCollection.class.getName() + "PARSED_MESSAGES_PROPERTY";
81  	public static final String PROP_DESCRIPTION = Hl7V2MessageCollection.class.getName() + "DESCRIPTION";
82  	public static final String PROP_ENCODING = Hl7V2MessageCollection.class.getName() + "ENCODING";
83  	public static final String PROP_HIGHLITED_PATH = Hl7V2MessageCollection.class.getName() + "HIGHLITED_PATH";
84  	public static final String PROP_HIGHLITED_RANGE = Hl7V2MessageCollection.class.getName() + "HIGHLITED_RANGE";
85  	public static final String PROP_SAVE_FILENAME = Hl7V2MessageCollection.class.getName() + "SAVE_FILENAME";
86  	public static final String PROP_VALIDATIONCONTEXT_OR_PROFILE = Hl7V2MessageCollection.class.getName() + "VALIDATIONCONTEXT";
87  	public static final String SAVED_PROPERTY = AbstractMessage.class.getName() + "SAVED_PROPERTY";
88  	public static final String SOURCE_MESSAGE_PROPERTY = Hl7V2MessageCollection.class.getName() + "SOURCE_MESSAGE_PROPERTY";
89  
90  	private Hl7V2EncodingTypeEnum myEncoding = Hl7V2EncodingTypeEnum.ER_7;
91  	private String myHighlitedPath;
92  	private Range myHighlitedRange;
93  	private String myId;
94  	private String myLastSendToInterfaceId;
95  	private String myMessageDescription;
96  	private List<Range> myMessageRanges = new ArrayList<Range>();
97  	private List<AbstractMessage<?>> myMessages = new ArrayList<AbstractMessage<?>>();
98  	private GenericParser myParser;
99  	private String myProfileId;
100 	private ProfileGroup myRuntimeProfile;
101 	private Charset mySaveCharset;
102 	private boolean mySaved;
103 	private String mySaveFileName;
104 	private long mySaveFileTimestamp;
105 	private LineEndingsEnum mySaveLineEndings;
106 	private int mySendNumberOfTimes = 1;
107 	private ShowEnum myShow;
108 	private String mySourceMessage;
109 	private boolean myStripSaveComments;
110 	private ValidationContext myValidationContext = new DefaultValidation();
111 
112 	
113 
114 
115 	public Hl7V2MessageCollection() {
116 		myParser = new GenericParser();
117 		myParser.setValidationContext(new ValidationContextImpl());
118 
119 		myId = UUID.randomUUID().toString();
120 	}
121 
122 	public synchronized void addComment(String theComment) {
123 		String oldValue = mySourceMessage;
124 
125 		ensureSourceMessage();
126 
127 		StringBuilder b = new StringBuilder();
128 		if (myEncoding == Hl7V2EncodingTypeEnum.ER_7) {
129 			for (String next : theComment.split("\\n")) {
130 				if (StringUtils.isNotBlank(next)) {
131 					b.append("#");
132 					b.append(next);
133 					b.append("\n");
134 				} else {
135 					b.append("\n");
136 				}
137 			}
138 
139 		} else {
140 			b.append("<!--");
141 			b.append(theComment);
142 			b.append("-->\n");
143 		}
144 
145 		int originalLength = mySourceMessage.length();
146 		mySourceMessage += b.toString();
147 
148 		Comment comment = new Comment();
149 		comment.setSourceMessage(theComment);
150 		myMessages.add(comment);
151 
152 		ensureSourceMessage();
153 
154 		myMessageRanges.add(new Range(originalLength, originalLength + b.length()));
155 
156 		firePropertyChange(SOURCE_MESSAGE_PROPERTY, oldValue, mySourceMessage);
157 		firePropertyChange(PARSED_MESSAGES_PROPERTY, oldValue, mySourceMessage);
158 	}
159 
160 	public synchronized void addMessage(AbstractMessage<?> theMessage) {
161 		Validate.notNull(theMessage);
162 
163 		int newIndex = myMessages.size();
164 		myMessages.add(theMessage);
165 
166 		ensureSourceMessage();
167 
168 		myMessageRanges.add(new Range(mySourceMessage.length()));
169 
170 		if (theMessage instanceof Hl7V2MessageBase) {
171 			updateSourceMessageBasedOnParsedMessage(newIndex, (Message) theMessage.getParsedMessage());
172 		}
173 	}
174 
175 	public void clearHighlight() {
176 		Range oldValue = myHighlitedRange;
177 		myHighlitedRange = null;
178 		firePropertyChange(PROP_HIGHLITED_RANGE, oldValue, myHighlitedRange);
179 
180 	}
181 
182 	public int countMessagesOfType(Class<Hl7V2MessageBase> theClass) {
183 		int retVal = 0;
184 		for (AbstractMessage<?> next : getMessages()) {
185 			if (theClass.isAssignableFrom(next.getClass())) {
186 				retVal++;
187 			}
188 		}
189 		return retVal;
190 	}
191 
192 	private synchronized void doSetSourceMessageEr7(String message) {
193 		String[] lines = message.split("\\r");
194 
195 		StringBuilder buf = new StringBuilder();
196 		ParserState curState = ParserState.NONE;
197 		int curStart = 0;
198 		for (String nextLine : lines) {
199 			ParserState newState;
200 			if (nextLine.startsWith("#")) {
201 				newState = ParserState.COMMENT;
202 			} else if (nextLine.startsWith("MSH")) {
203 				newState = ParserState.AT_MSG_START;
204 			} else {
205 				if (nextLine.trim().length() > 0) {
206 					if (curState == ParserState.IN_MSG && nextLine.matches("^[A-Z][A-Z0-9][A-Z0-9].*")) {
207 						newState = ParserState.IN_MSG;
208 					} else {
209 						newState = ParserState.UNKNOWN;
210 					}
211 				} else {
212 					newState = ParserState.NONE;
213 				}
214 			}
215 
216 			if (curState != newState) {
217 				AbstractMessage<?> msg = null;
218 				String bufToString = buf.toString();
219 				if (bufToString.length() > 0) {
220 					switch (curState) {
221 					case AT_MSG_START:
222 					case IN_MSG:
223 						try {
224 
225 							
226 							
227 							
228 							
229 							
230 							ourLog.info("Found ER7 message");
231 							Hl7V2MessageBase hl7msg = new Hl7V2MessageEr7();
232 							hl7msg.setIndexWithinCollection(myMessages.size());
233 							hl7msg.setRuntimeProfile(myRuntimeProfile);
234 							hl7msg.setSourceMessage(bufToString);
235 							msg = hl7msg;
236 
237 						} catch (PropertyVetoException e) {
238 							msg = new UnknownMessage(bufToString);
239 						}
240 						break;
241 					case COMMENT:
242 						msg = new Comment(bufToString);
243 						break;
244 					case UNKNOWN:
245 						msg = new UnknownMessage(bufToString);
246 						break;
247 					}
248 
249 					if (msg != null) {
250 						myMessages.add(msg);
251 						myMessageRanges.add(new Range(curStart, curStart + bufToString.length()));
252 					}
253 				}
254 
255 				curStart = curStart + bufToString.length();
256 				buf.setLength(0);
257 
258 				if (newState == ParserState.AT_MSG_START) {
259 					curState = ParserState.IN_MSG;
260 				} else {
261 					curState = newState;
262 				}
263 			}
264 
265 			buf.append(nextLine).append('\r');
266 
267 		}
268 
269 		String bufToString = buf.toString();
270 		if (bufToString.trim().length() > 0) {
271 			AbstractMessage<?> msg = null;
272 			switch (curState) {
273 			case IN_MSG:
274 			case AT_MSG_START:
275 				try {
276 
277 					ourLog.info("Found ER7 message");
278 					Hl7V2MessageBase hl7msg = new Hl7V2MessageEr7();
279 					hl7msg.setIndexWithinCollection(myMessages.size());
280 					hl7msg.setRuntimeProfile(myRuntimeProfile);
281 					hl7msg.setSourceMessage(bufToString);
282 					msg = hl7msg;
283 
284 				} catch (PropertyVetoException e) {
285 					msg = new UnknownMessage(bufToString);
286 				}
287 				break;
288 			case COMMENT:
289 				msg = new Comment(bufToString);
290 				break;
291 			case UNKNOWN:
292 				msg = new UnknownMessage(bufToString);
293 				break;
294 			}
295 
296 			if (msg != null) {
297 				myMessages.add(msg);
298 				myMessageRanges.add(new Range(curStart, curStart + bufToString.length()));
299 			}
300 
301 		}
302 
303 		
304 		
305 		
306 		
307 		
308 		
309 		
310 		
311 		
312 		
313 		
314 		
315 		
316 		
317 		
318 		
319 		
320 		
321 		
322 		
323 		
324 		
325 		
326 		
327 		
328 		
329 		
330 		
331 		
332 		
333 		
334 		
335 		
336 		
337 		
338 		
339 		
340 		
341 	}
342 
343 	private synchronized void doSetSourceMessageXml(String theMessage) {
344 		int rangeStart = 0;
345 		int rangeEnd = 0;
346 
347 		Pattern p = Pattern.compile("<([A-Za-z0-9_]+).*?>");
348 		Matcher m = p.matcher(theMessage);
349 
350 		while (rangeStart < theMessage.length() && m.find(rangeStart)) {
351 
352 			int startIndex = m.start();
353 			String tagName = m.group(1);
354 
355 			Pattern endP = Pattern.compile("</" + tagName + "(\\s.*?)?>");
356 			Matcher endM = endP.matcher(theMessage);
357 			boolean foundEnd = endM.find(startIndex);
358 
359 			if (foundEnd) {
360 
361 				String fullMsg = theMessage.substring(startIndex, endM.end());
362 				Hl7V2MessageXml nextMsg = new Hl7V2MessageXml();
363 				nextMsg.setIndexWithinCollection(myMessages.size());
364 				nextMsg.setRuntimeProfile(myRuntimeProfile);
365 				nextMsg.setEncoding(Hl7V2EncodingTypeEnum.XML);
366 				try {
367 					nextMsg.setSourceMessage(fullMsg);
368 					myMessages.add(nextMsg);
369 				} catch (PropertyVetoException e) {
370 					UnknownMessage nextUnk = new UnknownMessage(fullMsg);
371 					myMessages.add(nextUnk);
372 				}
373 
374 				rangeEnd = endM.end();
375 
376 			} else {
377 
378 				String fullMsg = theMessage.substring(startIndex);
379 				UnknownMessage nextUnk = new UnknownMessage(fullMsg);
380 				myMessages.add(nextUnk);
381 				rangeEnd = theMessage.length();
382 
383 			}
384 
385 			myMessageRanges.add(new Range(rangeStart, rangeEnd));
386 			rangeStart = rangeEnd;
387 
388 		}
389 
390 		
391 		
392 		
393 		
394 		
395 		
396 		
397 		
398 		
399 		
400 		
401 		
402 		
403 		
404 		
405 		
406 		
407 		
408 		
409 	}
410 
411 	private void ensureSourceMessage() {
412 		if (mySourceMessage == null) {
413 			mySourceMessage = "";
414 		}
415 	}
416 
417 	
418 
419 
420 	@Override
421 	public boolean equals(Object theObj) {
422 		if (!(theObj instanceof Hl7V2MessageCollection)) {
423 			return false;
424 		}
425 		Hl7V2MessageCollection o = (Hl7V2MessageCollection) theObj;
426 		return StringUtils.equals(myId, o.myId);
427 	}
428 
429 	@Override
430 	public String exportConfigToXml() {
431 		XmlFormat xml = exportCreate();
432 		return exportWrite(xml);
433 	}
434 
435 	public XmlFormat exportConfigToXmlWithoutContents() {
436 		XmlFormat xml = exportCreate();
437 		xml.mySourceMessage = null;
438 		return xml;
439 	}
440 
441 	private XmlFormat exportCreate() {
442 		XmlFormat xml = new XmlFormat();
443 
444 		xml.myId = this.myId;
445 
446 		if (getSaveFileName() == null) {
447 			xml.mySourceMessage = this.mySourceMessage.replaceAll("((\r\n)|\r)|(\n)", "\n");
448 		}
449 
450 		xml.mySaveCharsetName = mySaveCharset != null ? mySaveCharset.name() : "";
451 		xml.mySaved = this.mySaved;
452 		xml.mySaveFileName = StringUtils.isNotBlank(mySaveFileName) ? mySaveFileName : "";
453 		xml.myEncodingType = myEncoding.name();
454 		xml.myValidationContextClass = myValidationContext != null ? myValidationContext.getClass().getName() : "";
455 		xml.myProfileId = myProfileId != null ? myProfileId : "";
456 		xml.mySaveStripComments = Boolean.toString(myStripSaveComments);
457 		xml.mySaveLineEndings = mySaveLineEndings != null ? mySaveLineEndings.name() : "";
458 		xml.mySaveTimestamp = mySaveFileTimestamp;
459 		xml.myLastSendToInterfaceId = myLastSendToInterfaceId;
460 		xml.myEditorShowMode = getEditorShowMode();
461 		xml.mySendNumberOfTimes = mySendNumberOfTimes;
462 		return xml;
463 	}
464 
465 	private String exportWrite(XmlFormat xml) {
466 		StringWriter stringWriter = new StringWriter();
467 		JAXB.marshal(xml, stringWriter);
468 		String string = stringWriter.toString();
469 		return string;
470 	}
471 
472 	private synchronized int findSegmentMsgIndex(Message message) {
473 		int msgIndex = -1;
474 		for (int i = 0; i < myMessages.size(); i++) {
475 			Object nextParsed = myMessages.get(i).getParsedMessage();
476 			if (nextParsed == message) {
477 				msgIndex = i;
478 				break;
479 			}
480 		}
481 		return msgIndex;
482 	}
483 
484 	private String fixLineEndings(String theSourceMessage, LineEndingsEnum theEndings) {
485 		StringBuilder b = new StringBuilder(theSourceMessage.length());
486 
487 		for (int i = 0; i < theSourceMessage.length(); i++) {
488 
489 			char next = theSourceMessage.charAt(i);
490 			boolean isEnd = false;
491 			if (next == '\r') {
492 				isEnd = true;
493 			} else if (next == '\n') {
494 				if (i > 0 && theSourceMessage.charAt(i - 1) == '\r') {
495 					continue;
496 				} else {
497 					isEnd = true;
498 				}
499 			}
500 
501 			if (isEnd) {
502 				if (theEndings == LineEndingsEnum.UNIX) {
503 					b.append('\n');
504 				} else if (theEndings == LineEndingsEnum.HL7) {
505 					b.append('\r');
506 				} else if (theEndings == LineEndingsEnum.WINDOWS) {
507 					b.append('\r');
508 					b.append('\n');
509 				}
510 			} else {
511 				b.append(next);
512 			}
513 
514 		}
515 
516 		return b.toString();
517 	}
518 
519 	
520 
521 
522 
523 	public String getBestDescription() {
524 		if (mySaveFileName != null) {
525 			return mySaveFileName;
526 		}
527 		return getMessageDescription();
528 	}
529 
530 	
531 
532 
533 	public ShowEnum getEditorShowMode() {
534 		if (myShow == null) {
535 			myShow = ShowEnum.POPULATED;
536 		}
537 		return myShow;
538 	}
539 
540 	public Hl7V2EncodingTypeEnum getEncoding() {
541 		return myEncoding;
542 	}
543 
544 	public String getHighlitedPath() {
545 		return myHighlitedPath;
546 	}
547 
548 	public String getId() {
549 		return myId;
550 	}
551 
552 	
553 
554 
555 	public String getLastSendToInterfaceId() {
556 		return myLastSendToInterfaceId;
557 	}
558 
559 	public String getMessageDescription() {
560 		if (myMessageDescription == null) {
561 			updateMessageDescription();
562 		}
563 		return myMessageDescription;
564 	}
565 
566 	
567 
568 
569 	public List<Range> getMessageRanges() {
570 		return Collections.unmodifiableList(myMessageRanges);
571 	}
572 
573 	
574 
575 
576 	public List<AbstractMessage<?>> getMessages() {
577 		return myMessages;
578 	}
579 
580 	
581 
582 
583 	public ProfileGroup getRuntimeProfile() {
584 		return myRuntimeProfile;
585 	}
586 
587 	
588 
589 
590 	public Charset getSaveCharset() {
591 		return mySaveCharset;
592 	}
593 
594 	
595 
596 
597 	public String getSaveFileName() {
598 		return mySaveFileName;
599 	}
600 
601 	
602 
603 
604 	public long getSaveFileTimestamp() {
605 		return mySaveFileTimestamp;
606 	}
607 
608 	
609 
610 
611 	public LineEndingsEnum getSaveLineEndings() {
612 		return mySaveLineEndings;
613 	}
614 
615 	
616 
617 
618 	public int getSendNumberOfTimes() {
619 		if (mySendNumberOfTimes <= 0) {
620 			return 1;
621 		}
622 		return mySendNumberOfTimes;
623 	}
624 
625 	public String getSourceMessage() {
626 		return mySourceMessage;
627 	}
628 
629 	
630 
631 
632 	public ValidationContext getValidationContext() {
633 		return myValidationContext;
634 	}
635 
636 	
637 
638 
639 	@Override
640 	public int hashCode() {
641 		return new HashCodeBuilder().append(myId).toHashCode();
642 	}
643 
644 	
645 
646 
647 	public boolean isSaved() {
648 		return mySaved;
649 	}
650 
651 	
652 
653 
654 	public boolean isSaveStripComments() {
655 		return myStripSaveComments;
656 	}
657 
658 	public boolean isValidating() {
659 		return myValidationContext != null || myRuntimeProfile != null;
660 	}
661 
662 	
663 
664 
665 
666 	public void setEditorShowMode(ShowEnum theShow) {
667 		myShow = theShow;
668 	}
669 
670 	public void setEncoding(Hl7V2EncodingTypeEnum theEncoding) {
671 		Validate.notNull(theEncoding);
672 
673 		Hl7V2EncodingTypeEnum oldEncodingValue = myEncoding;
674 		if (myEncoding != theEncoding) {
675 			myEncoding = theEncoding;
676 
677 			switch (myEncoding) {
678 			case ER_7:
679 				myParser.setPipeParserAsPrimary();
680 				break;
681 			case XML:
682 				myParser.setXMLParserAsPrimary();
683 				break;
684 			}
685 
686 			StringBuilder b = new StringBuilder();
687 			myMessageRanges.clear();
688 
689 			for (int i = 0; i < myMessages.size(); i++) {
690 				int start = b.length();
691 
692 				AbstractMessage<?> nextObj = myMessages.get(i);
693 				if (nextObj instanceof Hl7V2MessageBase) {
694 
695 					Hl7V2MessageBase messageImpl = (Hl7V2MessageBase) nextObj;
696 					messageImpl = messageImpl.asEncoding(myEncoding);
697 
698 					b.append(messageImpl.getSourceMessage());
699 					myMessages.set(i, messageImpl);
700 
701 				} else if (nextObj instanceof UnknownMessage) {
702 
703 					UnknownMessage messageImpl = (UnknownMessage) nextObj;
704 					b.append(messageImpl.getSourceMessage());
705 
706 				}
707 
708 				b.append("\r\r");
709 				int end = b.length();
710 				myMessageRanges.add(new Range(start, end));
711 			}
712 
713 			String oldValue = mySourceMessage;
714 			mySourceMessage = b.toString();
715 			firePropertyChange(SOURCE_MESSAGE_PROPERTY, oldValue, mySourceMessage);
716 
717 			if (StringUtils.equals(oldValue, mySourceMessage) == false) {
718 				setSaved(false);
719 			}
720 
721 			firePropertyChange(PROP_ENCODING, oldEncodingValue, myEncoding);
722 		}
723 	}
724 
725 	public void setHighlitedPathBasedOnRange(Range theRange) {
726 
727 		final String oldValue = myHighlitedPath;
728 
729 		if (theRange == null) {
730 			myHighlitedPath = null;
731 		} else {
732 			for (int i = 0; i < myMessageRanges.size(); i++) {
733 				Range range = myMessageRanges.get(i);
734 				if (range.contains(theRange.getStart())) {
735 					AbstractMessage<?> am = myMessages.get(i);
736 					if (am instanceof Hl7V2MessageBase) {
737 						Hl7V2MessageBase messageImpl = (Hl7V2MessageBase) am;
738 						messageImpl.setHighlitedPathBasedOnRange(theRange.add(-range.getStart()));
739 						myHighlitedPath = i + messageImpl.getHighlitedPath();
740 					}
741 					break;
742 				}
743 			}
744 		} 
745 
746 		EventQueue.invokeLater(new Runnable() {
747 			public void run() {
748 				firePropertyChange(PROP_HIGHLITED_PATH, oldValue, myHighlitedPath);
749 				updateMessageDescription();
750 			}
751 		});
752 
753 	}
754 
755 	public void setHighlitedRangeBasedOnField(SegmentAndComponentPath thePath) {
756 		Segment segment = thePath.getSegment();
757 		Message message = segment.getMessage();
758 		int msgIndex = findSegmentMsgIndex(message);
759 
760 		Range oldValue = myHighlitedRange;
761 		if (msgIndex != -1) {
762 			Hl7V2MessageBase msg = (Hl7V2MessageBase) myMessages.get(msgIndex);
763 			msg.setHighlitedField(thePath);
764 			myHighlitedRange = msg.getHighlitedRange();
765 			if (myHighlitedRange != null) {
766 				myHighlitedRange = myHighlitedRange.add(myMessageRanges.get(msgIndex).getStart());
767 			} else {
768 				ourLog.info("No highlited range");
769 			}
770 		} else {
771 			ourLog.info("Couldn't find message");
772 		}
773 
774 		firePropertyChange(PROP_HIGHLITED_RANGE, oldValue, myHighlitedRange);
775 	}
776 
777 	public void setHighlitedRangeBasedOnSegment(Segment... theSegment) {
778 		Range oldValue = myHighlitedRange;
779 
780 		if (theSegment == null || theSegment.length == 0) {
781 			myHighlitedRange = null;
782 			firePropertyChange(PROP_HIGHLITED_RANGE, oldValue, myHighlitedRange);
783 			return;
784 		}
785 
786 		Message message = theSegment[0].getMessage();
787 		int msgIndex = findSegmentMsgIndex(message);
788 
789 		if (msgIndex != -1) {
790 			Hl7V2MessageBase msg = (Hl7V2MessageBase) myMessages.get(msgIndex);
791 			msg.setHighlitedRangeBasedOnSegment(theSegment);
792 			myHighlitedRange = msg.getHighlitedRange();
793 			if (myHighlitedRange != null) {
794 				myHighlitedRange = myHighlitedRange.add(myMessageRanges.get(msgIndex).getStart());
795 			} else {
796 				ourLog.info("No highlited range");
797 			}
798 		} else {
799 			ourLog.info("Couldn't find message");
800 		}
801 
802 		firePropertyChange(PROP_HIGHLITED_RANGE, oldValue, myHighlitedRange);
803 
804 	}
805 
806 	private void setId(String theId) {
807 		myId = theId;
808 	}
809 
810 	
811 
812 
813 
814 	public void setLastSendToInterfaceId(String theLastSendToInterfaceId) {
815 		myLastSendToInterfaceId = theLastSendToInterfaceId;
816 	}
817 
818 	public void setRuntimeProfile(ProfileGroup theProfileGroup) throws ProfileException {
819 		if (theProfileGroup == null && myRuntimeProfile == null && myValidationContext == null) {
820 			return;
821 		}
822 
823 		if (theProfileGroup == null && myRuntimeProfile == null && myValidationContext != null) {
824 			myValidationContext = null;
825 			myRuntimeProfile = null;
826 			myProfileId = null;
827 			firePropertyChange(PROP_VALIDATIONCONTEXT_OR_PROFILE, null, null);
828 			return;
829 		}
830 
831 		myRuntimeProfile = theProfileGroup;
832 		myValidationContext = null;
833 		myProfileId = theProfileGroup.getId();
834 
835 		for (AbstractMessage<?> next : myMessages) {
836 			if (next instanceof Hl7V2MessageBase) {
837 				((Hl7V2MessageBase) next).setRuntimeProfile(myRuntimeProfile);
838 			}
839 		}
840 
841 		firePropertyChange(PROP_VALIDATIONCONTEXT_OR_PROFILE, null, null);
842 	}
843 
844 	
845 
846 
847 
848 	public void setSaveCharset(Charset theSaveCharset) {
849 		mySaveCharset = theSaveCharset;
850 	}
851 
852 	
853 
854 
855 
856 	public void setSaved(boolean theSaved) {
857 		boolean oldValue = mySaved;
858 		mySaved = theSaved;
859 		firePropertyChange(SAVED_PROPERTY, oldValue, mySaved);
860 	}
861 
862 	
863 
864 
865 
866 	public void setSaveFileName(String theFileName) {
867 		String oldValue = null;
868 		mySaveFileName = theFileName;
869 		firePropertyChange(PROP_SAVE_FILENAME, oldValue, theFileName);
870 
871 		updateMessageDescription();
872 	}
873 
874 	
875 
876 
877 
878 	public void setSaveFileTimestamp(long theSaveFileTimestamp) {
879 		mySaveFileTimestamp = theSaveFileTimestamp;
880 	}
881 
882 	public void setSaveLineEndings(LineEndingsEnum theLineEndings) {
883 		mySaveLineEndings = theLineEndings;
884 	}
885 
886 	public void setSaveStripComments(boolean theSaveComments) {
887 		myStripSaveComments = theSaveComments;
888 	}
889 
890 	
891 
892 
893 
894 	public void setSendNumberOfTimes(int theSendNumberOfTimes) {
895 		mySendNumberOfTimes = theSendNumberOfTimes;
896 	}
897 
898 	public void setSourceMessage(String theSourceMessage) {
899 		ourLog.info("About to set source message for collection");
900 
901 		ArrayList<AbstractMessage<?>> oldMessages = new ArrayList<AbstractMessage<?>>(myMessages);
902 		myMessages.clear();
903 		myMessageRanges.clear();
904 
905 		String message = fixLineEndings(theSourceMessage, LineEndingsEnum.HL7);
906 
907 		if (myEncoding == Hl7V2EncodingTypeEnum.ER_7) {
908 			doSetSourceMessageEr7(message);
909 		} else {
910 			doSetSourceMessageXml(message);
911 		}
912 
913 		String oldValue = mySourceMessage;
914 		mySourceMessage = theSourceMessage;
915 
916 		ourLog.info("Firing message change event");
917 
918 		firePropertyChange(PARSED_MESSAGES_PROPERTY, oldMessages, myMessages);
919 		firePropertyChange(SOURCE_MESSAGE_PROPERTY, oldValue, mySourceMessage);
920 
921 		if (StringUtils.equals(oldValue, mySourceMessage) == false) {
922 			setSaved(false);
923 		}
924 
925 		updateMessageDescription();
926 
927 		ourLog.info("Done setting source message for collection");
928 	}
929 
930 	
931 
932 
933 
934 	public void setValidationContext(ValidationContext theValidationContext) {
935 		if (myValidationContext instanceof DefaultValidationpl/DefaultValidation.html#DefaultValidation">DefaultValidation && theValidationContext instanceof DefaultValidation) {
936 			return;
937 		}
938 
939 		if (theValidationContext != null) {
940 			myRuntimeProfile = null;
941 			myRuntimeProfile = null;
942 		}
943 
944 		ourLog.info("Setting validation context to: " + theValidationContext);
945 
946 		for (AbstractMessage<?> next : myMessages) {
947 			if (next instanceof Hl7V2MessageBase) {
948 				((Hl7V2MessageBase) next).setRuntimeProfile(null);
949 			}
950 		}
951 
952 		ValidationContext oldValue = myValidationContext;
953 		myValidationContext = theValidationContext;
954 		firePropertyChange(PROP_VALIDATIONCONTEXT_OR_PROFILE, oldValue, theValidationContext);
955 	}
956 
957 	
958 
959 
960 	@Override
961 	public String toString() {
962 		return Hl7V2MessageCollection.class.getSimpleName() + "[" + getSaveFileName() + "]";
963 	}
964 
965 	private void updateMessageDescription() {
966 		String oldValue = myMessageDescription;
967 
968 		Matcher matcher = FIRSTLINE_COMMENT_PATTERN.matcher(StringUtils.defaultString(mySourceMessage));
969 		if (matcher.find()) {
970 
971 			myMessageDescription = matcher.group(1).trim();
972 
973 		} else if (mySaveFileName == null) {
974 
975 			int msgs = 0;
976 			AbstractMessage<?> firstNonComment = null;
977 			for (AbstractMessage<?> next : myMessages) {
978 				if (!(next instanceof Comment)) {
979 					if (msgs == 0) {
980 						firstNonComment = next;
981 					}
982 					msgs++;
983 				}
984 			}
985 
986 			if (msgs == 0) {
987 				myMessageDescription = "None";
988 			} else if (msgs == 1) {
989 				if (firstNonComment instanceof UnknownMessage) {
990 					myMessageDescription = "Unknown";
991 				} else if (firstNonComment instanceof Hl7V2MessageBase) {
992 					myMessageDescription = ((Hl7V2MessageBase) firstNonComment).getMessageDescription();
993 				} else {
994 					myMessageDescription = "None";
995 				}
996 			} else {
997 				myMessageDescription = msgs + " messages";
998 			}
999 
1000 		} else {
1001 
1002 			myMessageDescription = mySaveFileName.replaceAll(".*(\\\\|\\/)", "");
1003 
1004 		}
1005 
1006 		firePropertyChange(PROP_DESCRIPTION, oldValue, myMessageDescription);
1007 	}
1008 
1009 	public void updateSourceMessage(String theNewSource, int theChangeStart, int theChangeEnd) {
1010 		
1011 		setSourceMessage(theNewSource);
1012 	}
1013 
1014 	private synchronized void updateSourceMessageBasedOnEncodedMessage(int theMessageIndex, String theEncodedMessage) {
1015 		setHighlitedPathBasedOnRange(null);
1016 
1017 		Range range = myMessageRanges.get(theMessageIndex);
1018 
1019 		String oldValue = mySourceMessage;
1020 
1021 		String preMessage = mySourceMessage.substring(0, range.getStart());
1022 		String postMessage = mySourceMessage.length() > range.getEnd() ? mySourceMessage.substring(range.getEnd()) : "";
1023 		mySourceMessage = preMessage + theEncodedMessage + postMessage;
1024 
1025 		int sizeDifference = theEncodedMessage.length() - range.getDelta();
1026 		for (int i = theMessageIndex; i < myMessageRanges.size(); i++) {
1027 			if (i == theMessageIndex) {
1028 				myMessageRanges.set(i, myMessageRanges.get(i).addToEnd(sizeDifference));
1029 			} else {
1030 				myMessageRanges.set(i, myMessageRanges.get(i).addToBoth(sizeDifference));
1031 			}
1032 		}
1033 
1034 		Hl7V2MessageBase msgBase = (Hl7V2MessageBase) myMessages.get(theMessageIndex);
1035 		try {
1036 			msgBase.setSourceMessage(theEncodedMessage);
1037 		} catch (PropertyVetoException e) {
1038 			ourLog.error("Failed to update message", e);
1039 		}
1040 
1041 		firePropertyChange(SOURCE_MESSAGE_PROPERTY, oldValue, mySourceMessage);
1042 
1043 		if (StringUtils.equals(oldValue, mySourceMessage) == false) {
1044 			setSaved(false);
1045 		}
1046 
1047 	}
1048 
1049 	public void updateSourceMessageBasedOnParsedMessage(int theMessageIndex, Message theMsg) {
1050 		String encodedMessage;
1051 		try {
1052 			encodedMessage = myParser.encode(theMsg);
1053 		} catch (HL7Exception e) {
1054 			ourLog.error("Failed to encode message", e);
1055 			return;
1056 		}
1057 
1058 		updateSourceMessageBasedOnEncodedMessage(theMessageIndex, encodedMessage);
1059 	}
1060 
1061 	
1062 
1063 
1064 	public void writeToFile(Writer theWriter, boolean theSelectedSaveStripComments, LineEndingsEnum theSelectedLineEndings) throws IOException {
1065 		String toWrite = mySourceMessage;
1066 
1067 		if (theSelectedSaveStripComments) {
1068 			switch (myEncoding) {
1069 			case ER_7:
1070 
1071 				Pattern p = Pattern.compile("(^|\\r)\\s*#[^\\r]*");
1072 				Matcher m = p.matcher(toWrite);
1073 				toWrite = m.replaceAll("").trim() + "\r";
1074 
1075 				p = Pattern.compile("\\r\\s*\\r");
1076 				m = p.matcher(toWrite);
1077 				toWrite = m.replaceAll("\r");
1078 
1079 				break;
1080 			}
1081 		}
1082 
1083 		toWrite = fixLineEndings(toWrite, theSelectedLineEndings);
1084 
1085 		theWriter.append(toWrite);
1086 	}
1087 
1088 	public static Hl7V2MessageCollection fromXml(ProfileFileList theProfileFileList, String theContents) {
1089 		XmlFormat xmlFormat = JAXB.unmarshal(new StringReader(theContents),XmlFormat.class);
1090 		return fromXml(theProfileFileList, xmlFormat);
1091 	}
1092 
1093 	public static Hl7V2MessageCollection fromXml(ProfileFileList theProfileFileList, XmlFormat theXmlFormat) {
1094 
1095 		Hl7V2MessageCollection retVal = new Hl7V2MessageCollection();
1096 
1097 		retVal.setSaveFileName(isNotBlank(theXmlFormat.mySaveFileName) ? theXmlFormat.mySaveFileName : null);
1098 		retVal.setId(theXmlFormat.myId);
1099 		retVal.setLastSendToInterfaceId(theXmlFormat.myLastSendToInterfaceId);
1100 		retVal.setEditorShowMode(theXmlFormat.myEditorShowMode);
1101 		retVal.setSendNumberOfTimes(theXmlFormat.mySendNumberOfTimes);
1102 
1103 		try {
1104 			retVal.setSaveCharset(isNotEmpty(theXmlFormat.mySaveCharsetName) ? Charset.forName(theXmlFormat.mySaveCharsetName) : null);
1105 		} catch (IllegalCharsetNameException e) {
1106 			
1107 		}
1108 
1109 		if (isNotBlank(theXmlFormat.myValidationContextClass)) {
1110 			retVal.setValidationContext(ClassUtils.instantiateOrReturnNull(theXmlFormat.myValidationContextClass, ValidationContext.class));
1111 		}
1112 
1113 		if (isNotBlank(theXmlFormat.myProfileId)) {
1114 			try {
1115 				retVal.setRuntimeProfile(theProfileFileList.getProfile(theXmlFormat.myProfileId));
1116 			} catch (ProfileException e) {
1117 				ourLog.error("Failed to retrieve profile with id: " + theXmlFormat.myProfileId, e);
1118 			}
1119 		}
1120 
1121 		try {
1122 			retVal.setSaveLineEndings(LineEndingsEnum.valueOf(theXmlFormat.mySaveLineEndings));
1123 		} catch (Exception e) {
1124 			retVal.setSaveLineEndings(LineEndingsEnum.HL7);
1125 		}
1126 
1127 		retVal.setSaveStripComments(Boolean.parseBoolean(theXmlFormat.mySaveStripComments));
1128 
1129 		retVal.setEncoding("XML".equals(theXmlFormat.myEncodingType) ? Hl7V2EncodingTypeEnum.XML : Hl7V2EncodingTypeEnum.ER_7);
1130 
1131 		if (StringUtils.isNotBlank(theXmlFormat.mySourceMessage)) {
1132 			retVal.setSourceMessage(theXmlFormat.mySourceMessage);
1133 		} else {
1134 			retVal.setSourceMessage("");
1135 		}
1136 
1137 		retVal.setSaveFileTimestamp(theXmlFormat.mySaveTimestamp);
1138 
1139 		
1140 		retVal.setSaved(theXmlFormat.mySaved);
1141 
1142 		return retVal;
1143 	}
1144 
1145 	public static void main(String[] args) {
1146 
1147 		System.out.println(Pattern.compile("<([A-Z0-9_]+)").matcher("<ORU_R01 xmlns=\"ffdf\">aaaaa").find());
1148 
1149 	}
1150 
1151 	private enum ParserState {
1152 		AT_MSG_START, COMMENT, IN_MSG, NONE, UNKNOWN
1153 	}
1154 
1155 	@XmlAccessorType(XmlAccessType.FIELD)
1156 	@XmlRootElement(name = "Hl7V2MessageCollection", namespace = "urn:ca.uhn.hapi:testpanel")
1157 	public static class XmlFormat {
1158 
1159 		@XmlAttribute(required = false, name = "editorShowMode")
1160 		public ShowEnum myEditorShowMode;
1161 
1162 		@XmlAttribute(required = true, name = "encodingType")
1163 		public String myEncodingType;
1164 
1165 		@XmlAttribute(required = true, name = "id")
1166 		public String myId;
1167 
1168 		@XmlAttribute(required = false, name = "lastSendToInterfaceId")
1169 		private String myLastSendToInterfaceId;
1170 
1171 		@XmlElement(required = true, name = "profileId")
1172 		public String myProfileId;
1173 
1174 		@XmlAttribute(required = true, name = "charsetName")
1175 		private String mySaveCharsetName;
1176 
1177 		@XmlAttribute(required = true, name = "saved")
1178 		private boolean mySaved;
1179 
1180 		@XmlAttribute(required = true, name = "saveFileName")
1181 		private String mySaveFileName;
1182 
1183 		@XmlAttribute(required = true, name = "saveLineEndings")
1184 		private String mySaveLineEndings;
1185 
1186 		@XmlAttribute(required = true, name = "saveStripComments")
1187 		private String mySaveStripComments;
1188 
1189 		@XmlAttribute(required = true, name = "saveFileTimestamp")
1190 		private long mySaveTimestamp;
1191 
1192 		@XmlAttribute(required = false, name = "sendNumberOfTimes")
1193 		public int mySendNumberOfTimes;
1194 
1195 		@XmlElement(required = true, name = "sourceMessage")
1196 		public String mySourceMessage;
1197 
1198 		@XmlElement(required = true, name = "validationContextClass")
1199 		public String myValidationContextClass;
1200 
1201 	}
1202 
1203 }