View Javadoc
1   package ca.uhn.hl7v2.testpanel.controller;
2   
3   import java.beans.PropertyChangeListener;
4   import java.beans.PropertyChangeSupport;
5   import java.io.BufferedReader;
6   import java.io.File;
7   import java.io.FileNotFoundException;
8   import java.io.FileReader;
9   
10  import javax.swing.SwingUtilities;
11  
12  import org.apache.commons.lang.StringUtils;
13  
14  import ca.uhn.hl7v2.HL7Exception;
15  import ca.uhn.hl7v2.testpanel.ui.tools.Hl7V2FileDiffDialog;
16  import ca.uhn.hl7v2.testpanel.util.CharCountingReaderWrapper;
17  import ca.uhn.hl7v2.testpanel.util.compare.BulkHl7V2Comparison;
18  import ca.uhn.hl7v2.testpanel.util.compare.Hl7V2MessageCompare;
19  import ca.uhn.hl7v2.testpanel.util.compare.IComparisonListener;
20  import ca.uhn.hl7v2.util.Hl7InputStreamMessageIterator;
21  import ca.uhn.hl7v2.util.Terser;
22  
23  public class Hl7V2FileDiffController {
24  
25  	private static final int OUTPUT_LINES = 1000;
26  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Hl7V2FileDiffController.class);
27  
28  	public static final String PROP_RUNNING = Hl7V2FileDiffController.class.getName() + "_RUNNING";
29  	public static final String PROP_FAILED = Hl7V2FileDiffController.class.getName() + "_FAILED";
30  	public static final String PROP_PERCENT_DONE = Hl7V2FileDiffController.class.getName() + "_PERCENT_DONE";
31  	public static final String PROP_OUTPUT = Hl7V2FileDiffController.class.getName() + "_OUTPUT";
32  
33  	private final PropertyChangeSupport myPcs = new PropertyChangeSupport(this);
34  	private boolean myRunning;
35  	private final Hl7V2FileDiffDialog myView;
36  	private final Controller myMasterController;
37  	private long myFile1Size;
38  	private long myFile2Size;
39  	private CharCountingReaderWrapper myCountingReader1;
40  	private CharCountingReaderWrapper myCountingReader2;
41  	private Hl7InputStreamMessageIterator myMessageIter1;
42  	private Hl7InputStreamMessageIterator myMessageIter2;
43  	private BulkHl7V2Comparison myCompare;
44  	private RunningThread myThread;
45  	private boolean myFailed;
46  	private int myPercentDone;
47  	private String[] myOutput = new String[OUTPUT_LINES];
48  	private int myOutputNextIndex = 0;
49  	private int myOutputSize = 0;
50  	private int myNumMessagesTotal;
51  	private int myNumMessagesPassed;
52  	private int myNumMessagesFailed;
53  
54  	public Hl7V2FileDiffController(Controller theMasterController) {
55  		myMasterController = theMasterController;
56  		myView = new Hl7V2FileDiffDialog(this);
57  	}
58  
59  	public void addOutput(final String... theLines) {
60  		if (!SwingUtilities.isEventDispatchThread()) {
61  			SwingUtilities.invokeLater(new Runnable() {
62  				public void run() {
63  					addOutput(theLines);
64  				}
65  			});
66  			return;
67  		}
68  
69  		if (theLines == null || theLines.length == 0) {
70  			return;
71  		}
72  
73  		for (String string : theLines) {
74  			myOutput[myOutputNextIndex] = string;
75  			if (myOutputSize < OUTPUT_LINES) {
76  				myOutputSize++;
77  			}
78  			myOutputNextIndex++;
79  			myOutputNextIndex %= OUTPUT_LINES;
80  		}
81  
82  		myPcs.firePropertyChange(PROP_OUTPUT, null, null);
83  	}
84  
85  	/**
86  	 * @return the outputSize
87  	 */
88  	public int getOutputSize() {
89  		return myOutputSize;
90  	}
91  
92  	public String getOutputLine(int theIndex) {
93  		if (myOutputSize < OUTPUT_LINES) {
94  			return myOutput[theIndex];
95  		} else {
96  			int index = (myOutputNextIndex + theIndex) % OUTPUT_LINES;
97  			return myOutput[index];
98  		}
99  	}
100 
101 	public void addPropertyChangeListener(String theProperty, PropertyChangeListener listener) {
102 		this.myPcs.addPropertyChangeListener(theProperty, listener);
103 	}
104 
105 	/**
106 	 * @return the running
107 	 */
108 	public boolean isRunning() {
109 		return myRunning;
110 	}
111 
112 	public void removePropertyChangeListener(String theProperty, PropertyChangeListener listener) {
113 		this.myPcs.removePropertyChangeListener(theProperty, listener);
114 	}
115 
116 	private void setRunning(boolean theRunning) {
117 		boolean oldValue = myRunning;
118 		myRunning = theRunning;
119 		myPcs.firePropertyChange(PROP_RUNNING, oldValue, theRunning);
120 	}
121 
122 	public void show() {
123 		myView.setVisible(true);
124 		myView.requestFocusInWindow();
125 	}
126 
127 	public void begin() {
128 		assert SwingUtilities.isEventDispatchThread() : "Shouldn't be called from " + Thread.currentThread().getName();
129 
130 		File file1 = new File(myView.getFile1Text());
131 		if (file1.exists() == false) {
132 			myMasterController.showDialogError("File does not exist: " + myView.getFile1Text());
133 			return;
134 		}
135 		if (file1.isDirectory()) {
136 			myMasterController.showDialogError("File is a directory: " + myView.getFile1Text());
137 			return;
138 		}
139 
140 		File file2 = new File(myView.getFile2Text());
141 		if (file2.exists() == false) {
142 			myMasterController.showDialogError("File does not exist: " + myView.getFile2Text());
143 			return;
144 		}
145 		if (file2.isDirectory()) {
146 			myMasterController.showDialogError("File is a directory: " + myView.getFile2Text());
147 			return;
148 		}
149 
150 		myFile1Size = file1.length();
151 		myFile2Size = file2.length();
152 
153 		try {
154 			myCountingReader1 = new CharCountingReaderWrapper(new BufferedReader(new FileReader(file1)));
155 			myCountingReader2 = new CharCountingReaderWrapper(new BufferedReader(new FileReader(file2)));
156 		} catch (FileNotFoundException e) {
157 			myMasterController.showDialogError(e.getMessage());
158 			return;
159 		}
160 
161 		myMessageIter1 = new Hl7InputStreamMessageIterator(myCountingReader1);
162 		myMessageIter1.setIgnoreComments(true);
163 		myMessageIter2 = new Hl7InputStreamMessageIterator(myCountingReader2);
164 		myMessageIter2.setIgnoreComments(true);
165 
166 		myCompare = new BulkHl7V2Comparison();
167 		myCompare.setStopOnFirstFailure(myView.isStopOnFirstError());
168 		myCompare.setExpectedMessages(myMessageIter1);
169 		myCompare.setActualMessages(myMessageIter2);
170 		myCompare.setExpectedAndActualDescription("File 1", "File 2");
171 
172 		setRunning(true);
173 		setFailed(false);
174 		clearOutput();
175 		
176 		myNumMessagesTotal = 0;
177 		myNumMessagesFailed = 0;
178 		myNumMessagesPassed = 0;
179 
180 		myThread = new RunningThread();
181 		myThread.start();
182 
183 	}
184 
185 	private void clearOutput() {
186 		myOutputNextIndex = 0;
187 		myOutputSize = 0;
188 		myPcs.firePropertyChange(PROP_OUTPUT, null, null);
189 	}
190 
191 	/**
192 	 * @return the percentDone
193 	 */
194 	public int getPercentDone() {
195 		return myPercentDone;
196 	}
197 
198 	/**
199 	 * @param thePercentDone
200 	 *            the percentDone to set
201 	 */
202 	public void setPercentDone(int thePercentDone) {
203 		int oldValue = myPercentDone;
204 		myPercentDone = thePercentDone;
205 		myPcs.firePropertyChange(PROP_PERCENT_DONE, oldValue, thePercentDone);
206 	}
207 
208 	private void setFailed(boolean theFailed) {
209 		boolean oldValue = myFailed;
210 		myFailed = theFailed;
211 		myPcs.firePropertyChange(PROP_FAILED, oldValue, theFailed);
212 	}
213 
214 	/**
215 	 * @return the failed
216 	 */
217 	public boolean isFailed() {
218 		return myFailed;
219 	}
220 
221 	private class RunningThread extends Thread implements IComparisonListener {
222 
223 		@Override
224 		public void run() {
225 
226 			try {
227 				addOutput("Beginning comparison");
228 
229 				myCompare.addComparisonListener(this);
230 				try {
231 					myCompare.compare();
232 				} catch (Exception e) {
233 					markFailed();
234 					ourLog.error("Unexpected test failure: ", e);
235 					addOutput("Comparison failed unexpectedly, this is probably a bug: " + e.getMessage());
236 					return;
237 				}
238 
239 				if (isRunning() && !isFailed()) {
240 					addOutput("No differences found");
241 				}
242 
243 			} finally {
244 				SwingUtilities.invokeLater(new Runnable() {
245 					public void run() {
246 						setRunning(false);
247 					}
248 				});
249 			}
250 		}
251 
252 		public void failure(Hl7V2MessageCompare theComparison) {
253 			myNumMessagesTotal++;
254 			myNumMessagesFailed++;
255 
256 			markFailed();
257 			updatePercentDone();
258 			cancelIfNeccesary();
259 
260 			addOutput(" ");
261 			
262 			String controlId1 = "", controlId2 = "";
263 			try {
264 				controlId1 = new Terser(theComparison.getExpectedMessage()).get("/MSH-10");
265 				controlId2 = new Terser(theComparison.getActualMessage()).get("/MSH-10");
266 			} catch (HL7Exception e) {
267 				// ignore
268 			}
269 			
270 			StringBuilder b= new StringBuilder();
271 			b.append("<html><span style=\"color: red;\">Difference Found in message " + myNumMessagesTotal);
272 			b.append(" (Control ID ").append(controlId1);
273 			if (StringUtils.equals(controlId1, controlId2) == false) {
274 				b.append(" / ").append(controlId2);
275 			}
276 			b.append(")");
277 			b.append("</span></html>");
278 			addOutput(b.toString());
279 			
280 			if (myView.isShowWholeMessageOnError()) {
281 				addOutput("Message in file 1:");
282 				try {
283 					addOutput(theComparison.getExpectedMessage().printStructure().split("\\n"));
284 				} catch (HL7Exception e) {
285 					ourLog.error("Failed to print structure", e);
286 				}
287 				
288 				addOutput(" ");
289 				addOutput("Message in file 2:");
290 				try {
291 					addOutput(theComparison.getExpectedMessage().printStructure().split("\\n"));
292 				} catch (HL7Exception e) {
293 					ourLog.error("Failed to print structure", e);
294 				}
295 				
296 				addOutput(" ");
297 				addOutput("Differences:");
298 			}
299 			
300 			String difference = theComparison.describeDifference();
301 			addOutput(difference.split("\\n"));
302 
303 		}
304 
305 		public void success(Hl7V2MessageCompare theComparison) {
306 			myNumMessagesTotal++;
307 			myNumMessagesPassed++;
308 			
309 			updatePercentDone();
310 			cancelIfNeccesary();
311 		}
312 
313 		private void cancelIfNeccesary() {
314 			if (!isRunning()) {
315 				myCompare.cancel();
316 			}
317 		}
318 
319 		private void updatePercentDone() {
320 			int percent = (int) ((myCountingReader1.getCount() * 100) / myFile1Size);
321 			setPercentDone(percent);
322 		}
323 
324 		public void markFailed() {
325 			SwingUtilities.invokeLater(new Runnable() {
326 				public void run() {
327 					setFailed(true);
328 				}
329 			});
330 		}
331 
332 		public void progressLog(String theMsgLine) {
333 //			addOutput(theMsgLine);
334 		}
335 
336 	}
337 
338 	public void cancel() {
339 		setRunning(false);
340 	}
341 
342 }