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.statcvs.input;
24
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.SortedSet;
32
33 import net.sf.statcvs.model.Commit;
34 import net.sf.statcvs.model.Revision;
35
36 /**
37 * Takes a set of revisions, and builds a <code>List</code> of
38 * {@link Commit}s from it. The result list is sorted by date.
39 *
40 * The implementation allows for a tolerance of several minutes
41 * between individual file commits, but author and message must be identical.
42 *
43 * @author Richard Cyganiak
44 * @version $Id: CommitListBuilder.java,v 1.5 2008/04/02 11:22:15 benoitx Exp $
45 */
46 public class CommitListBuilder {
47 private static final int MAX_TIME_BETWEEN_CHANGES_MILLISECONDS = 300000;
48
49 private final Iterator revisions;
50 private final Map currentCommits = new HashMap();
51 private List commits;
52
53 /**
54 * Creates a new instance using the given set of {@link Revision}s.
55 * The set must be sorted by date, oldest first.
56 *
57 * @param revisions a set of {@link Revision}s
58 */
59 public CommitListBuilder(final SortedSet revisions) {
60 this(revisions.iterator());
61 }
62
63 public CommitListBuilder(final Iterator revisions) {
64 this.revisions = revisions;
65 }
66
67 /**
68 * Creates a <code>List</code> of {@link Commit}s from the source iterator.
69 * The result list will be sorted by date.
70 *
71 * @return a new list of {@link Commit} objects
72 */
73 public List createCommitList() {
74 if (commits != null) {
75 return commits;
76 }
77
78 commits = new LinkedList();
79 while (revisions.hasNext()) {
80 processRevision((Revision) revisions.next());
81 }
82 return commits;
83 }
84
85 protected void processRevision(final Revision rev) {
86 if (rev.getAuthor() == null) {
87 return;
88 }
89 final Commit commit = (Commit) currentCommits.get(rev.getAuthor());
90 if (commit == null || !isSameCommit(commit, rev)) {
91 addNewCommit(rev);
92 } else {
93 addRevToCommit(commit, rev);
94 }
95 }
96
97 protected void addNewCommit(final Revision rev) {
98 final Commit newCommit = new Commit(rev);
99 currentCommits.put(rev.getAuthor(), newCommit);
100 commits.add(newCommit);
101 }
102
103 protected void addRevToCommit(final Commit commit, final Revision rev) {
104 commit.addRevision(rev);
105 }
106
107 /**
108 * Returns <code>true</code> if change is part of the commit, that is if
109 * they have the same author, the same message, and are within the same
110 * timeframe.
111 *
112 * @param commit the commit
113 * @param rev the revision to check against this commit
114 * @return <code>true</code> if change is part of this commit
115 */
116 public static boolean isSameCommit(final Commit commit, final Revision rev) {
117 return commit.getAuthor().equals(rev.getAuthor()) && commit.getComment().equals(rev.getComment()) && isInTimeFrame(commit, rev.getDate());
118 }
119
120 /**
121 * Returns <code>true</code> if the date lies within the timespan of
122 * the commit, plus/minus a tolerance.
123 *
124 * @param date the date to check against this commit
125 * @return <code>true</code> if the date lies within the timespan of the commit
126 */
127 public static boolean isInTimeFrame(final Commit commit, final Date date) {
128 return date.getTime() > (commit.getDate().getTime() - MAX_TIME_BETWEEN_CHANGES_MILLISECONDS)
129 && (date.getTime() < commit.getDate().getTime() + MAX_TIME_BETWEEN_CHANGES_MILLISECONDS);
130 }
131 }