1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package net.sf.statsvn.input;
24
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Date;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.ListIterator;
31 import java.util.Map;
32 import java.util.SortedSet;
33 import java.util.TreeSet;
34
35 import net.sf.statcvs.input.NoLineCountException;
36 import net.sf.statcvs.model.Revision;
37 import net.sf.statcvs.model.SymbolicName;
38 import net.sf.statcvs.model.VersionedFile;
39 import net.sf.statsvn.output.SvnConfigurationOptions;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class FileBuilder {
72 private static final int ONE_SECOND = 1000;
73
74 private static final int ONE_MIN_IN_MS = 60000;
75
76 private final Builder builder;
77
78 private final String name;
79
80 private boolean binary;
81
82 private final List revisions = new ArrayList();
83
84 private final Map revBySymnames;
85
86 private final Map dateBySymnames;
87
88 private int locDelta;
89
90
91
92
93
94
95
96
97
98
99
100
101 public FileBuilder(final Builder builder, final String name, final boolean isBinary, final Map revBySymnames, final Map dateBySymnames) {
102 this.builder = builder;
103 this.name = name;
104 this.binary = isBinary;
105 this.revBySymnames = revBySymnames;
106 this.dateBySymnames = dateBySymnames;
107
108 SvnConfigurationOptions.getTaskLogger().log("logging " + name);
109 }
110
111
112
113
114
115
116
117
118 public void addRevisionData(final RevisionData data) {
119 if (binary && !data.isCreationOrRestore()) {
120 data.setLines(0, 0);
121 }
122 this.revisions.add(data);
123
124 locDelta += getLOCChange(data);
125 }
126
127
128
129
130
131
132
133
134
135
136
137 public VersionedFile createFile(final Date beginOfLogDate) {
138 if (isFilteredFile() || !fileExistsInLogPeriod()) {
139 return null;
140 }
141
142 final VersionedFile file = new VersionedFile(name, builder.getDirectory(name));
143
144 if (revisions.isEmpty()) {
145 buildBeginOfLogRevision(file, beginOfLogDate, getFinalLOC(), null);
146 return file;
147 }
148
149 final Iterator it = revisions.iterator();
150 RevisionData currentData = (RevisionData) it.next();
151 int currentLOC = getFinalLOC();
152 RevisionData previousData;
153 int previousLOC;
154 SortedSet symbolicNames;
155
156 while (it.hasNext()) {
157 previousData = currentData;
158 previousLOC = currentLOC;
159 currentData = (RevisionData) it.next();
160 currentLOC = previousLOC - getLOCChange(previousData);
161
162
163 symbolicNames = createSymbolicNamesCollection(previousData);
164
165 if (previousData.isCreationOrRestore() || previousData.isChange() || isBinary()) {
166 if (currentData.isDeletion()) {
167 buildCreationRevision(file, previousData, previousLOC, symbolicNames);
168 } else {
169 buildChangeRevision(file, previousData, previousLOC, symbolicNames);
170 }
171 } else if (previousData.isDeletion()) {
172 buildDeletionRevision(file, previousData, previousLOC, symbolicNames);
173 } else {
174 SvnConfigurationOptions.getTaskLogger().info("illegal state in " + file.getFilenameWithPath() + ":" + previousData.getRevisionNumber());
175 }
176 }
177
178
179 symbolicNames = createSymbolicNamesCollection(currentData);
180
181 final int nextLinesOfCode = currentLOC - getLOCChange(currentData);
182 if (currentData.isCreationOrRestore()) {
183 buildCreationRevision(file, currentData, currentLOC, symbolicNames);
184 } else if (currentData.isDeletion()) {
185 buildDeletionRevision(file, currentData, currentLOC, symbolicNames);
186 buildBeginOfLogRevision(file, beginOfLogDate, nextLinesOfCode, symbolicNames);
187 } else if (currentData.isChange()) {
188 buildChangeRevision(file, currentData, currentLOC, symbolicNames);
189 currentData.setDate(new Date(currentData.getDate().getTime() - ONE_SECOND));
190 buildCreationRevision(file, currentData, 0, symbolicNames);
191 buildBeginOfLogRevision(file, beginOfLogDate, nextLinesOfCode, symbolicNames);
192 } else {
193 SvnConfigurationOptions.getTaskLogger().info("illegal state in " + file.getFilenameWithPath() + ":" + currentData.getRevisionNumber());
194 }
195 return file;
196 }
197
198
199
200
201
202
203
204
205
206 private int getFinalLOC() {
207 if (binary) {
208 return 0;
209 }
210
211 String revision = null;
212 try {
213 revision = builder.getRevision(name);
214 } catch (final IOException e) {
215 if (!finalRevisionIsDead()) {
216 SvnConfigurationOptions.getTaskLogger().info(e.getMessage());
217 }
218 }
219
220 try {
221
222
223
224 if (!revisions.isEmpty()) {
225 final RevisionData firstAdded = (RevisionData) revisions.get(0);
226 if (!finalRevisionIsDead() && !firstAdded.getRevisionNumber().equals(revision)) {
227 SvnConfigurationOptions.getTaskLogger().info("Revision of " + name + " does not match expected revision");
228 }
229 }
230 return builder.getLOC(name);
231
232 } catch (final NoLineCountException e) {
233 if (!finalRevisionIsDead()) {
234 SvnConfigurationOptions.getTaskLogger().info(e.getMessage());
235 }
236 return approximateFinalLOC();
237 }
238 }
239
240
241
242
243
244
245 protected boolean finalRevisionIsDead() {
246 if (revisions.isEmpty()) {
247 return false;
248 }
249 return ((RevisionData) revisions.get(0)).isDeletion();
250 }
251
252
253
254
255
256
257 public boolean existRevision() {
258 return !revisions.isEmpty();
259 }
260
261
262
263
264
265
266
267
268
269
270 private int approximateFinalLOC() {
271 int max = 0;
272 int current = 0;
273 final Iterator it = revisions.iterator();
274 while (it.hasNext()) {
275 final RevisionData data = (RevisionData) it.next();
276 current += data.getLinesAdded();
277 max = Math.max(current, max);
278 current -= data.getLinesRemoved();
279 }
280 return max;
281 }
282
283
284
285
286
287
288
289
290
291
292 private int getLOCChange(final RevisionData data) {
293 return data.getLinesAdded() - data.getLinesRemoved();
294 }
295
296 private void buildCreationRevision(final VersionedFile file, final RevisionData data, final int loc, final SortedSet symbolicNames) {
297 file.addInitialRevision(data.getRevisionNumber(), builder.getAuthor(data.getLoginName()), data.getDate(), data.getComment(), loc, symbolicNames);
298 }
299
300 private void buildChangeRevision(final VersionedFile file, final RevisionData data, final int loc, final SortedSet symbolicNames) {
301 file.addChangeRevision(data.getRevisionNumber(), builder.getAuthor(data.getLoginName()), data.getDate(), data.getComment(), loc, data.getLinesAdded()
302 - data.getLinesRemoved(), Math.min(data.getLinesAdded(), data.getLinesRemoved()), symbolicNames);
303 }
304
305 private void buildDeletionRevision(final VersionedFile file, final RevisionData data, final int loc, final SortedSet symbolicNames) {
306 file.addDeletionRevision(data.getRevisionNumber(), builder.getAuthor(data.getLoginName()), data.getDate(), data.getComment(), loc, symbolicNames);
307 }
308
309 private void buildBeginOfLogRevision(final VersionedFile file, final Date beginOfLogDate, final int loc, final SortedSet symbolicNames) {
310 final Date date = new Date(beginOfLogDate.getTime() - ONE_MIN_IN_MS);
311 final Revision dummyForMove = file.addBeginOfLogRevision(date, loc, symbolicNames);
312
313
314
315
316 if (symbolicNames != null) {
317 final Iterator it = symbolicNames.iterator();
318 while (it.hasNext()) {
319 ((SymbolicName) it.next()).getRevisions().remove(dummyForMove);
320 }
321 }
322 }
323
324
325
326
327
328
329
330 private boolean isFilteredFile() {
331 return !this.builder.matchesPatterns(this.name);
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345 private boolean fileExistsInLogPeriod() {
346 if (revisions.size() > 0 || binary) {
347 return true;
348 }
349 try {
350 builder.getLOC(name);
351 return true;
352 } catch (final NoLineCountException fileDoesNotExistInTimespan) {
353 return false;
354 }
355 }
356
357
358
359
360
361
362
363
364
365
366 private SortedSet createSymbolicNamesCollection(final RevisionData revisionData) {
367 SortedSet symbolicNames = null;
368
369 final int currentRevision = getRevisionAsInt(revisionData.getRevisionNumber());
370 SvnConfigurationOptions.getTaskLogger().log("\n" + name + " CURRENT REVISION = " + currentRevision + " Deleted " + revisionData.isDeletion());
371
372 if (revisions.isEmpty()) {
373 SvnConfigurationOptions.getTaskLogger().log("NO Revisions....");
374 return symbolicNames;
375 }
376
377
378 for (final Iterator tags = revBySymnames.entrySet().iterator(); tags.hasNext();) {
379 final Map.Entry tag = (Map.Entry) tags.next();
380
381 final int tagRevision = getRevisionAsInt((String) tag.getValue());
382
383 SvnConfigurationOptions.getTaskLogger().log("Considering tag REV " + tagRevision + " name=" + tag.getKey());
384
385
386
387 int previousRevisionForThisFile = getRevisionAsInt(((RevisionData) revisions.get(revisions.size() - 1)).getRevisionNumber());
388 int revisionToTag = -1;
389 for (final ListIterator it = revisions.listIterator(revisions.size()); it.hasPrevious();) {
390 final RevisionData data = (RevisionData) it.previous();
391
392 SvnConfigurationOptions.getTaskLogger().log(
393 "File REV " + data.getRevisionNumber() + " =>" + data.getDate() + " vs " + tagRevision + " Deletion:" + data.isDeletion());
394
395 final int dataRev = getRevisionAsInt(data.getRevisionNumber());
396
397 if (revisionData.isDeletion() && currentRevision < dataRev) {
398
399
400
401 previousRevisionForThisFile = getRevisionAsInt(data.getRevisionNumber());
402 continue;
403 } else if (dataRev == tagRevision) {
404 revisionToTag = tagRevision;
405 break;
406 } else if (dataRev > tagRevision && tagRevision >= previousRevisionForThisFile) {
407 revisionToTag = previousRevisionForThisFile;
408 SvnConfigurationOptions.getTaskLogger().log("1/ Revision to TAG " + revisionToTag);
409 break;
410 }
411
412 previousRevisionForThisFile = getRevisionAsInt(data.getRevisionNumber());
413 }
414
415
416
417 if (previousRevisionForThisFile < tagRevision && !revisionData.isDeletion()) {
418 revisionToTag = previousRevisionForThisFile;
419 SvnConfigurationOptions.getTaskLogger().log("2/ Revision to TAG " + revisionToTag);
420 }
421
422 SvnConfigurationOptions.getTaskLogger().log("Revision to TAG " + revisionToTag);
423
424 if (revisionToTag > 0 && revisionToTag == currentRevision) {
425
426 if (symbolicNames == null) {
427 symbolicNames = new TreeSet();
428 }
429 SvnConfigurationOptions.getTaskLogger().log(
430 "adding revision " + name + "," + currentRevision + " to symname " + tag.getKey() + " Date:" + dateBySymnames.get(tag.getKey()) + " A:"
431 + revisionData.getLinesAdded() + " R:" + revisionData.getLinesRemoved());
432 symbolicNames.add(builder.getSymbolicName((String) tag.getKey(), (Date) dateBySymnames.get(tag.getKey())));
433 }
434 }
435
436 return symbolicNames;
437 }
438
439 private int getRevisionAsInt(final String revisionNumber) {
440 int rev = 0;
441 if (revisionNumber != null && !revisionNumber.equals("0.0")) {
442 rev = Integer.valueOf(revisionNumber).intValue();
443 }
444 return rev;
445 }
446
447
448
449
450
451
452
453
454 public String getName() {
455 return name;
456 }
457
458
459
460
461
462
463
464
465 public List getRevisions() {
466 return revisions;
467 }
468
469
470
471
472
473
474
475
476 private RevisionData findRevision(final String revisionNumber) {
477 for (int i = 0; i < revisions.size(); i++) {
478 final RevisionData data = (RevisionData) revisions.get(i);
479 if (data.getRevisionNumber().equals(revisionNumber)) {
480 return data;
481 }
482 }
483 return null;
484 }
485
486
487
488
489
490
491
492
493
494
495 public synchronized boolean isBinary() {
496 return binary;
497 }
498
499
500
501
502
503
504
505
506
507 public synchronized void setBinary(final boolean isBinary) {
508 this.binary = isBinary;
509 }
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526 public void updateRevision(final String revisionNumber, final int linesAdded, final int linesRemoved) {
527 final RevisionData data = findRevision(revisionNumber);
528 if (data != null) {
529 data.setLines(linesAdded, linesRemoved);
530 }
531 }
532
533 }