1 /*
2 StatCvs - CVS statistics generation
3 Copyright (C) 2002 Lukasz Pekacki <lukasz@pekacki.de>
4 http://statcvs.sf.net/
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 $RCSfile: Revision.java,v $
21 Created on $Date: 2008/04/02 11:22:16 $
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.2 2008/04/02 11:22:16 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 //TODO: remove all deprecated methods when they are no longer used by StatCvs-XML
321
322 /**
323 * @deprecated Use {@link #getLinesDelta()} and {@link #getReplacedLines()} instead.
324 */
325 public int getLinesAdded() {
326 if (isInitialRevision() && getPreviousRevision() != null) {
327 return 0;
328 }
329 return getNewLines();
330 }
331
332 /**
333 * @deprecated Use {@link #getLinesDelta()} and {@link #getReplacedLines()} instead.
334 */
335 public int getLinesRemoved() {
336 if (isDead()) {
337 return 0;
338 }
339 if (getLinesDelta() < 0) {
340 return -getLinesDelta() + getReplacedLines();
341 }
342 return getReplacedLines();
343 }
344
345 /**
346 * @deprecated Use {@link #getLines()} instead.
347 */
348 public int getLinesOfCode() {
349 if (isDead() && getPreviousRevision() != null) {
350 return getPreviousRevision().getLines();
351 }
352 return getLines();
353 }
354
355 /**
356 * @deprecated Use {@link #getLines()} instead.
357 */
358 public int getEffectiveLinesOfCode() {
359 return getLines();
360 }
361
362 /**
363 * @deprecated Use {@link #getLinesDelta()} instead.
364 */
365 public int getLinesOfCodeChange() {
366 return getLinesDelta();
367 }
368
369 /**
370 * @deprecated Use {@link #getNewLines()} instead.
371 */
372 public int getLineValue() {
373 return getNewLines();
374 }
375
376 /**
377 * @deprecated Use {@link #getReplacedLines()} and {@link #getLinesDelta} instead.
378 */
379 public int getRemovingValue() {
380 if (getLinesDelta() > 0) {
381 return getReplacedLines();
382 }
383 return -getLinesDelta() + getReplacedLines();
384 }
385
386 /**
387 * @deprecated Use {@link #getFileCountDelta()} instead.
388 */
389 public int getFileCountChange() {
390 return getFileCountDelta();
391 }
392
393 /**
394 * @deprecated Use {@link #getRevisionNumber()} instead.
395 */
396 public String getRevision() {
397 return getRevisionNumber();
398 }
399 }