1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.aequo.text;
14
15 import java.beans.PropertyChangeListener;
16 import java.beans.PropertyChangeSupport;
17 import java.io.File;
18 import java.io.FileReader;
19 import java.io.FileWriter;
20 import java.io.IOException;
21 import java.io.Reader;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25
26 import org.abstracthorizon.aequo.CompareEntry;
27 import org.abstracthorizon.aequo.DefaultCompareModel;
28 import org.abstracthorizon.aequo.file.FileCompareEntry;
29 import org.incava.util.diff.Diff;
30 import org.incava.util.diff.Difference;
31
32
33
34
35
36
37 public class TextModel extends DefaultCompareModel<String, TextCompareEntry> {
38
39
40 public static int BUFFER_SIZE = 10240;
41
42
43 protected static final String EMPTY_STRING = "";
44
45
46 protected File fileLeft;
47
48
49 protected File fileRight;
50
51
52 protected int firstDifferenceBlock;
53
54
55 protected int lastDifferenceBlock;
56
57
58 protected PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
59
60
61 protected boolean leftFileDirty;
62
63
64 protected boolean rightFileDirty;
65
66
67 protected FileCompareEntry fileEntry;
68
69
70
71
72
73
74 public TextModel(File leftFile, File rightFile) {
75 super(2);
76 this.fileLeft = leftFile;
77 this.fileRight = rightFile;
78 }
79
80
81
82
83
84 public TextModel(FileCompareEntry fileCompareEntry) {
85 this(fileCompareEntry.getData(0), fileCompareEntry.getData(1));
86 this.fileEntry = fileCompareEntry;
87 }
88
89
90
91
92
93 public File getLeftFile() {
94 return fileLeft;
95 }
96
97
98
99
100
101 public File getRightFile() {
102 return fileRight;
103 }
104
105
106
107
108
109 public boolean isLeftFileDirty() {
110 return leftFileDirty;
111 }
112
113
114
115
116
117 public void setLeftFileDirty(boolean leftFileDirty) {
118 if (this.leftFileDirty != leftFileDirty) {
119 boolean oldValue = this.leftFileDirty;
120 this.leftFileDirty = leftFileDirty;
121 propertyChangeSupport.firePropertyChange("leftFileDirty", oldValue, leftFileDirty);
122 }
123 }
124
125
126
127
128
129 public boolean isRightFileDirty() {
130 return rightFileDirty;
131 }
132
133
134
135
136
137 public void setRightFileDirty(boolean rightFileDirty) {
138 if (this.rightFileDirty != rightFileDirty) {
139 boolean oldValue = this.rightFileDirty;
140 this.rightFileDirty = rightFileDirty;
141 propertyChangeSupport.firePropertyChange("rightFileDirty", oldValue, rightFileDirty);
142 }
143 }
144
145
146
147
148
149 public void setFileDirty(int column) {
150 if (column == 0) {
151 setLeftFileDirty(true);
152 } else if (column == 1) {
153 setRightFileDirty(true);
154 }
155 }
156
157
158
159
160 protected void refreshFileEntry() {
161 if (getSize() > 0) {
162 boolean equals = true;
163 int i = 0;
164 while (equals && (i < getSize())) {
165 CompareEntry<String> entry = get(i);
166 i++;
167 equals = ((entry.getStatus(0) == entry.getStatus(1)) && (entry.getStatus(0) == CompareEntry.EQUAL));
168 }
169
170 if (equals) {
171 if ((fileEntry.getStatus(0) != CompareEntry.EQUAL)
172 || (fileEntry.getStatus(1) != CompareEntry.EQUAL)) {
173 fileEntry.setStatus(0, CompareEntry.EQUAL);
174 fileEntry.setStatus(1, CompareEntry.EQUAL);
175 fileEntry.updateEntryStatus(true);
176 }
177 } else {
178 if ((fileEntry.getStatus(0) == CompareEntry.EQUAL)
179 || (fileEntry.getStatus(1) == CompareEntry.EQUAL)) {
180
181 fileEntry.setStatus(0, CompareEntry.DIFFERENT);
182 fileEntry.setStatus(1, CompareEntry.DIFFERENT);
183 fileEntry.updateEntryStatus(true);
184 }
185 }
186 }
187 }
188
189
190
191
192
193
194
195 public void addPropertyChangeListener(PropertyChangeListener listener) {
196 propertyChangeSupport.addPropertyChangeListener(listener);
197 }
198
199
200
201
202
203
204 public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
205 propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
206 }
207
208
209
210
211
212 public void removePropertyChangeListener(PropertyChangeListener listener) {
213 propertyChangeSupport.removePropertyChangeListener(listener);
214 }
215
216
217
218
219
220
221 public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
222 propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
223 }
224
225
226
227
228
229
230
231
232 public void saveAll() throws IOException {
233 saveLeft();
234 saveRight();
235 }
236
237
238
239
240
241 public void saveLeft() throws IOException {
242 if (fileLeft == null) {
243 throw new IllegalArgumentException("File name is not suppliled");
244 }
245 List<String> dataLeft = getAsList(0);
246 saveFile(fileLeft, dataLeft);
247 setLeftFileDirty(false);
248 if (fileEntry != null) {
249 refreshFileEntry();
250 }
251 }
252
253
254
255
256
257
258 public void saveRight() throws IOException {
259 if (fileRight == null) {
260 throw new IllegalArgumentException("File name is not suppliled");
261 }
262 List<String> dataRight = getAsList(1);
263 saveFile(fileRight, dataRight);
264 setRightFileDirty(false);
265 if (fileEntry != null) {
266 refreshFileEntry();
267 }
268 }
269
270
271
272
273
274 public void load() throws IOException {
275
276 final List<String> dataLeft = loadFile(fileLeft);
277 final List<String> dataRight = loadFile(fileRight);
278 refresh(dataLeft, dataRight);
279 setLeftFileDirty(false);
280 setRightFileDirty(false);
281 if (fileEntry != null) {
282 refreshFileEntry();
283 }
284 }
285
286
287
288
289
290
291 public void refresh(final List<String> dataLeft, final List<String> dataRight) {
292 entries.clear();
293 Runnable process = new Runnable() {
294
295 int ls = 0;
296 int rs = 0;
297 int le = 0;
298 int re = 0;
299
300 int l = 0;
301 int r = 0;
302
303 public void run() {
304 Diff<String> diff = new Diff<String>(dataLeft, dataRight);
305 List<Difference> diffs = diff.diff();
306
307 for (Difference d : diffs) {
308 rs = d.getAddedStart();
309 ls = d.getDeletedStart();
310 progressTo(ls, rs);
311
312 re = d.getAddedEnd();
313 le = d.getDeletedEnd();
314 progressTo(le, re);
315 }
316
317 int s1 = dataLeft.size();
318 int s2 = dataRight.size();
319
320 progressTo(s1, s2);
321
322 }
323
324 protected void progressTo(int le, int re) {
325 while ((le > l) || (re > r)) {
326 TextCompareEntry entry;
327 if ((le > l) && (re > r)) {
328 entry = new TextCompareEntry(new String[]{dataLeft.get(l), dataRight.get(r)}, new int[]{l, r});
329 entry.updateEntryStatus();
330 l = l + 1;
331 r = r + 1;
332 } else if (le > l) {
333 entry = new TextCompareEntry(new String[]{dataLeft.get(l), null}, new int[]{l, r});
334 entry.updateEntryStatus();
335 l = l + 1;
336 } else {
337 entry = new TextCompareEntry(new String[]{null, dataRight.get(r)}, new int[]{l, r});
338 entry.updateEntryStatus();
339 r = r + 1;
340 }
341 addImpl(entry);
342 }
343 }
344 };
345
346 process.run();
347 }
348
349
350
351
352
353
354
355 public void refreshRange(final int start, final int end) {
356 Runnable process = new Runnable() {
357
358 int ls = 0;
359 int rs = 0;
360 int le = 0;
361 int re = 0;
362
363 int l = 0;
364 int r = 0;
365
366 List<String> dataLeft;
367 List<String> dataRight;
368
369 public void run() {
370 dataLeft = getAsSubList(0, start, end);
371 dataRight = getAsSubList(1, start, end);
372
373 for (int i = end; i >= start; i--) {
374 removeImpl(i);
375 }
376
377 int current = start;
378
379 Diff<String> diff = new Diff<String>(dataLeft, dataRight);
380 List<Difference> diffs = diff.diff();
381
382 for (Difference d : diffs) {
383 rs = d.getAddedStart();
384 ls = d.getDeletedStart();
385 current = progressTo(current, ls, rs);
386
387 re = d.getAddedEnd();
388 le = d.getDeletedEnd();
389 current = progressTo(current, le, re);
390 }
391
392 int s1 = dataLeft.size();
393 int s2 = dataRight.size();
394
395 current = progressTo(current, s1, s2);
396 updateLineNumbers(start, end);
397 getSelectionModel().setSelectionInterval(start, current - 1);
398 }
399
400 protected int progressTo(int current, int le, int re) {
401 while ((le > l) || (re > r)) {
402 TextCompareEntry entry;
403 int[] lineNumbers = new int[]{-1, -1};
404 if ((le > l) && (re > r)) {
405 entry = new TextCompareEntry(new String[]{dataLeft.get(l), dataRight.get(r)}, lineNumbers);
406 entry.updateEntryStatus();
407 l = l + 1;
408 r = r + 1;
409 } else if (le > l) {
410 entry = new TextCompareEntry(new String[]{dataLeft.get(l), null}, lineNumbers);
411 entry.updateEntryStatus();
412 l = l + 1;
413 } else {
414 entry = new TextCompareEntry(new String[]{null, dataRight.get(r)}, lineNumbers);
415 entry.updateEntryStatus();
416 r = r + 1;
417 }
418 addImpl(current, entry);
419 current++;
420 }
421 return current;
422 }
423 };
424
425 process.run();
426 }
427
428
429
430
431
432
433
434 public static void saveFile(File file, List<String> data) throws IOException {
435 FileWriter fileWriter = new FileWriter(file);
436 try {
437 for (String line : data) {
438 fileWriter.write(line);
439 fileWriter.write('\n');
440 }
441 } finally {
442 fileWriter.close();
443 }
444 }
445
446
447
448
449
450
451
452 public static List<String> loadFile(File file) throws IOException {
453 if (file.exists() && file.isFile()) {
454 FileReader in = new FileReader(file);
455 return loadFile(in);
456 } else {
457 return Collections.emptyList();
458 }
459 }
460
461
462
463
464
465
466
467 public static List<String> loadFile(Reader in) throws IOException {
468 ArrayList<String> list = new ArrayList<String>();
469
470 StringBuffer sb = null;
471 char buf[] = new char[BUFFER_SIZE];
472 try {
473 int start = 0;
474 int r = in.read(buf);
475 while (r > 0) {
476 int i = 0;
477 while (i < r) {
478 if (buf[i] == '\n') {
479 if (i == start) {
480 if (sb == null) {
481 list.add(EMPTY_STRING);
482 } else {
483 list.add(sb.toString());
484 sb = null;
485 }
486 } else {
487 if (sb == null) {
488 list.add(new String(buf, start, i - start));
489 } else {
490 sb.append(buf, start, i - start);
491 list.add(sb.toString());
492 sb = null;
493 }
494 }
495
496 start = i + 1;
497 }
498 i = i + 1;
499 }
500
501 if (start < r) {
502 if (sb == null) {
503 sb = new StringBuffer(buf.length - start + (buf.length - start) / 2);
504 sb.append(buf, start, buf.length - start);
505 }
506 start = 0;
507 }
508 r = in.read(buf);
509 }
510 } finally {
511 in.close();
512 }
513
514 return list;
515 }
516
517
518
519
520
521
522 public void updateLineNumbers(int start, int end) {
523 if (end < 0) {
524 end = getSize() - 1;
525 }
526 int leftLineNumber = 1;
527 int rightLineNumber = 1;
528 if (start > 0) {
529 TextCompareEntry entry = get(start -1);
530 leftLineNumber = entry.getLineNumber(0) + 1;
531 rightLineNumber = entry.getLineNumber(1) + 1;
532 }
533 for (int i = start; i <= end; i++) {
534 TextCompareEntry entry = get(i);
535 entry.setLineNumber(0, leftLineNumber);
536 entry.setLineNumber(1, rightLineNumber);
537 if (entry.getData(0) != null) {
538 leftLineNumber++;
539 }
540 if (entry.getData(1) != null) {
541 rightLineNumber++;
542 }
543 }
544 }
545
546 }