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.model;
24
25 import java.util.Date;
26 import java.util.Iterator;
27 import java.util.SortedSet;
28
29 /**
30 * One revision of a {@link VersionedFile}. That can be an initial revision
31 * (checkin), a change, a deletion, or a re-add. Revisions are created
32 * using the methods {@link VersionedFile#addInitialRevision},
33 * {@link VersionedFile#addChangeRevision} and
34 * {@link VersionedFile#addDeletionRevision}.
35 *
36 * TODO: Replace type code with hierarchy
37 * TODO: Rename class to Revision, getAuthor() to getLogin(), isDead() to isDeletion()
38 *
39 * @author Manuel Schulze
40 * @author Richard Cyganiak <richard@cyganiak.de>
41 * @version $Id: Revision.java,v 1.3 2009/08/20 17:44:05 benoitx Exp $
42 */
43 public class Revision implements Comparable {
44
45 /**
46 * Marks a revision that creates a new file. The file did not exist
47 * in the current branch before this revision, and it does exist
48 * afterwards. Possibly the file existed before, that is, it was
49 * deleted and restored.
50 */
51 public static final int TYPE_CREATION = 1;
52
53 /**
54 * Marks a revision that changes the file. It does neither create nor
55 * delete the file.
56 */
57 public static final int TYPE_CHANGE = 2;
58
59 /**
60 * Marks a revision that deletes the file. The file existed before, but
61 * does not exist afterwards in the current branch.
62 */
63 public static final int TYPE_DELETION = 3;
64
65 /**
66 * Marks a revision at the very beginning of the log timespan. This is
67 * only a container for the number of code lines at the beginning of
68 * the log. It is not a real revision committed by an author.
69 */
70 public static final int TYPE_BEGIN_OF_LOG = 5;
71
72 private final VersionedFile file;
73 private final String revisionNumber;
74 private final int type;
75 private final Author author;
76 private final Date date;
77 private final String comment;
78 private final int lines;
79 private final int linesReplaced;
80 private final int linesDelta;
81
82 private final SortedSet symbolicNames;
83
84 /**
85 * Creates a new revision of a file with the
86 * specified revision number. Should not be called directly. Instead,
87 * {@link VersionedFile#addInitialRevision} and its sister methods should
88 * be used.
89 * @param file VersionedFile that belongs to this revision
90 * @param revisionNumber revision number, for example "1.1"
91 * @param type a <tt>TYPE_XXX</tt> constant
92 * @param author the author of the revision
93 * @param date the date of the revision
94 * @param comment the author's comment
95 * @param lines number of lines; 0 for deletions
96 * @param linesDelta by how much did the number of lines change, compared to the previous revision?
97 * @param linesReplaced How many lines were removed and replaced by other lines, without the delta changing?
98 * @param symbolicNames list of symbolic names for this revision or null if this revision has no symbolic names
99 */
100 public Revision(final VersionedFile file, final String revisionNumber, final int type, final Author author, final Date date, final String comment,
101 final int lines, final int linesDelta, final int linesReplaced, final SortedSet symbolicNames) {
102 this.file = file;
103 this.revisionNumber = revisionNumber;
104 this.type = type;
105 this.author = author;
106 this.date = date;
107 this.comment = comment;
108 this.lines = lines;
109 this.linesDelta = linesDelta;
110 this.linesReplaced = linesReplaced;
111 this.symbolicNames = symbolicNames;
112
113 if (author != null) {
114 author.addRevision(this);
115 }
116
117 if (symbolicNames != null) {
118 final Iterator it = symbolicNames.iterator();
119 while (it.hasNext()) {
120 ((SymbolicName) it.next()).addRevision(this);
121 }
122 }
123 }
124
125 /**
126 * Returns the revision number.
127 * @return the revision number
128 */
129 public String getRevisionNumber() {
130 return revisionNumber;
131 }
132
133 /**
134 * Returns the author of this revision.
135 * @return the author
136 */
137 public Author getAuthor() {
138 return author;
139 }
140
141 /**
142 * Returns the comment for this revision.
143 * @return the comment
144 */
145 public String getComment() {
146 return comment;
147 }
148
149 /**
150 * Returns the date of this revision.
151 * @return the date
152 */
153 public Date getDate() {
154 return date;
155 }
156
157 /**
158 * Returns the number of lines for this revision. This is 0 for
159 * dead revisions.
160 *
161 * @return the number of lines
162 */
163 public int getLines() {
164 return lines;
165 }
166
167 /**
168 * Returns by how many lines the line count changed with this
169 * revision. Deletions return <code>-getLines()</code>,
170 * re-adds and initial revisions return <code>getLines()</code>.
171 *
172 * @return the line count change of this revision
173 */
174 public int getLinesDelta() {
175 return linesDelta;
176 }
177
178 /**
179 * Returns the number of lines that were removed and replaced
180 * by other lines in this revision. For example, if 5 lines were
181 * added and 2 lines removed, this would be 3. If 1 line was added
182 * and 1 was removed, it would be 1. If it was an initial revision
183 * or a deletion, it would be 0.
184 *
185 * @return the number of lines that were replaced by other lines.
186 */
187 public int getReplacedLines() {
188 return linesReplaced;
189 }
190
191 /**
192 * Returns the number of "new" lines in this revision. This is the
193 * sum of added and changed lines. In other words, all the "original"
194 * lines the author of this revision came up with.
195 * @return lines changed or added
196 */
197 public int getNewLines() {
198 if (getLinesDelta() > 0) {
199 return getLinesDelta() + getReplacedLines();
200 }
201 return getReplacedLines();
202 }
203
204 /**
205 * Returns the change of the file count caused by this revision.
206 * This is 1 for initial revisions and re-adds, -1 for deletions,
207 * and 0 for normal revisions.
208 * @return the file count change of this revision
209 */
210 public int getFileCountDelta() {
211 if (isInitialRevision()) {
212 return 1;
213 } else if (isDead()) {
214 return -1;
215 } else {
216 return 0;
217 }
218 }
219
220 /**
221 * Returns <code>true</code> if the file did not exist before this
222 * revision and does exist afterwards. Possibly the file was deleted
223 * before, or it never existed before.
224 *
225 * @return <code>true</code> if the file did not exist before
226 */
227 public boolean isInitialRevision() {
228 return type == TYPE_CREATION;
229 }
230
231 /**
232 * Returns <tt>true</tt> if the file is deleted in this revision.
233 * @return <code>true</code> if the file is deleted in this revision
234 */
235 public boolean isDead() {
236 return type == TYPE_DELETION;
237 }
238
239 /**
240 * Returns <tt>true</tt> if this is a revision
241 * at the very beginning of the log timespan which is
242 * only a container for the number of code lines at the beginning
243 * of the log and not a real revision committed by an author.
244 * @return <code>true</code> if this revision exists
245 * only for StatCvs bookkeeping purposes
246 */
247 public boolean isBeginOfLog() {
248 return type == TYPE_BEGIN_OF_LOG;
249 }
250
251 /**
252 * {@inheritDoc}
253 */
254 public String toString() {
255 return this.author.getName() + " - " + this.revisionNumber;
256 }
257
258 /**
259 * Returns the file which was changed by this revision.
260 * @return the file
261 */
262 public VersionedFile getFile() {
263 return file;
264 }
265
266 /**
267 * Returns the predecessor of this revision or <tt>null</tt> if it
268 * is the first revision for the file.
269 * @return the predecessor of this revision
270 */
271 public Revision getPreviousRevision() {
272 return file.getPreviousRevision(this);
273 }
274
275 /**
276 * Returns a list of {@link SymbolicName}s of this revision or null if
277 * the revision has no symbolic names. The list is ordered from
278 * latest to oldest.
279 *
280 * @return list of symbolic names
281 */
282 public SortedSet getSymbolicNames() {
283 return symbolicNames;
284 }
285
286 /**
287 * Compares this revision to another revision. A revision is considered
288 * smaller if its date is smaller. If the dates are identical, the filename,
289 * author name, revision number and comment will be used to break the tie.
290 */
291 public int compareTo(final Object other) {
292 if (this == other) {
293 return 0;
294 }
295 final Revision otherRevision = (Revision) other;
296 int result = date.compareTo(otherRevision.getDate());
297 if (result != 0) {
298 return result;
299 }
300 result = file.getFilenameWithPath().compareTo(otherRevision.getFile().getFilenameWithPath());
301 if (result != 0) {
302 return result;
303 }
304 result = revisionNumber.compareTo(otherRevision.getRevisionNumber());
305 if (result != 0) {
306 return result;
307 }
308 if (author != null && otherRevision.getAuthor() != null) {
309 result = author.compareTo(otherRevision.getAuthor());
310 if (result != 0) {
311 return result;
312 }
313 }
314 if (comment != null && otherRevision.getComment() != null) {
315 return comment.compareTo(otherRevision.getComment());
316 }
317 return 1;
318 }
319
320 public boolean equals(final Object rhs) {
321 if (rhs == null) {
322 return false;
323 }
324 if (!(rhs instanceof Revision)) {
325 return false;
326 }
327 final Revision that = (Revision) rhs;
328
329 boolean eq = getDate().equals(that.getDate());
330
331 if (eq) {
332 eq = file.getFilenameWithPath().equals(that.file.getFilenameWithPath());
333 }
334 if (eq) {
335 eq = revisionNumber.equals(that.getRevisionNumber());
336 }
337 if (eq) {
338 eq = author != null && author.equals(that.getAuthor());
339 }
340 return eq;
341 }
342
343 public int hashCode() {
344 return getDate().hashCode() + file.hashCode() + revisionNumber.hashCode();
345 }
346
347
348
349 /**
350 * @deprecated Use {@link #getLinesDelta()} and {@link #getReplacedLines()} instead.
351 */
352 public int getLinesAdded() {
353 if (isInitialRevision() && getPreviousRevision() != null) {
354 return 0;
355 }
356 return getNewLines();
357 }
358
359 /**
360 * @deprecated Use {@link #getLinesDelta()} and {@link #getReplacedLines()} instead.
361 */
362 public int getLinesRemoved() {
363 if (isDead()) {
364 return 0;
365 }
366 if (getLinesDelta() < 0) {
367 return -getLinesDelta() + getReplacedLines();
368 }
369 return getReplacedLines();
370 }
371
372 /**
373 * @deprecated Use {@link #getLines()} instead.
374 */
375 public int getLinesOfCode() {
376 if (isDead() && getPreviousRevision() != null) {
377 return getPreviousRevision().getLines();
378 }
379 return getLines();
380 }
381
382 /**
383 * @deprecated Use {@link #getLines()} instead.
384 */
385 public int getEffectiveLinesOfCode() {
386 return getLines();
387 }
388
389 /**
390 * @deprecated Use {@link #getLinesDelta()} instead.
391 */
392 public int getLinesOfCodeChange() {
393 return getLinesDelta();
394 }
395
396 /**
397 * @deprecated Use {@link #getNewLines()} instead.
398 */
399 public int getLineValue() {
400 return getNewLines();
401 }
402
403 /**
404 * @deprecated Use {@link #getReplacedLines()} and {@link #getLinesDelta} instead.
405 */
406 public int getRemovingValue() {
407 if (getLinesDelta() > 0) {
408 return getReplacedLines();
409 }
410 return -getLinesDelta() + getReplacedLines();
411 }
412
413 /**
414 * @deprecated Use {@link #getFileCountDelta()} instead.
415 */
416 public int getFileCountChange() {
417 return getFileCountDelta();
418 }
419
420 /**
421 * @deprecated Use {@link #getRevisionNumber()} instead.
422 */
423 public String getRevision() {
424 return getRevisionNumber();
425 }
426 }