1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package net.sf.statcvs.model;
21
22 import java.util.Date;
23 import java.util.HashSet;
24 import java.util.Set;
25 import java.util.SortedSet;
26 import java.util.TreeSet;
27
28 /**
29 * Represents one versioned file in the {@link Repository Repository},
30 * including its name, {@link Directory} and {@link Revision} list.
31 * Revisions can be created using the <tt>addXXXRevision</tt> factory
32 * methods. Revisions can be created in any order.
33 *
34 * TODO: Rename class to something like VersionedFile, getCurrentLinesOfCode() to getCurrentLines(), maybe getFilenameXXX, isDead() to isDeleted()
35 *
36 * @author Manuel Schulze
37 * @author Richard Cyganiak <richard@cyganiak.de>
38 * @version $Id: VersionedFile.java,v 1.3 2009/03/09 21:45:42 benoitx Exp $
39 */
40 public class VersionedFile implements Comparable {
41 private final String filename;
42 private final SortedSet revisions = new TreeSet();
43 private final Directory directory;
44 private final Set authors = new HashSet();
45
46 /**
47 * Creates a VersionedFile object.
48 *
49 * @param name The full name of the file
50 * @param directory the directory where the file resides
51 */
52 public VersionedFile(final String name, final Directory directory) {
53 this.filename = name;
54 this.directory = directory;
55 if (directory != null) {
56 directory.addFile(this);
57 }
58 }
59
60 /**
61 * Returns a list of authors that have commited at least one revision of the file.
62 * @return a list of authors
63 */
64 public Set getAuthors() {
65 return authors;
66 }
67
68 /**
69 * Returns the full filename.
70 * @return the full filename
71 */
72 public String getFilenameWithPath() {
73 return filename;
74 }
75
76 /**
77 * Returns the filename without path.
78 * @return the filename without path
79 */
80 public String getFilename() {
81 final int lastDelim = this.filename.lastIndexOf("/");
82 return this.filename.substring(lastDelim + 1, this.filename.length());
83 }
84
85 /**
86 * Returns the file's <tt>Directory</tt>.
87 * @return the file's <tt>Directory</tt>
88 */
89 public Directory getDirectory() {
90 return directory;
91 }
92
93 /**
94 * Gets the latest revision of this file.
95 * @return the latest revision of this file
96 */
97 public Revision getLatestRevision() {
98 return (Revision) this.revisions.last();
99 }
100
101 /**
102 * Gets the earliest revision of this file.
103 * @return the earliest revision of this file
104 */
105 public Revision getInitialRevision() {
106 return (Revision) this.revisions.first();
107 }
108
109 /**
110 * Returns the list of {@link Revision}s of this file,
111 * sorted from earliest to most recent.
112 * @return a <tt>SortedSet</tt> of {@link Revision}s
113 */
114 public SortedSet getRevisions() {
115 return this.revisions;
116 }
117
118 /**
119 * Returns the current number of lines for this file. Binary files
120 * and deleted files are assumed to have 0 lines.
121 * @return the current number of lines for this file
122 */
123 public int getCurrentLinesOfCode() {
124 return getLatestRevision().getLines();
125 }
126
127 /**
128 * Returns <code>true</code> if the latest revision of this file was
129 * a deletion.
130 * @return <code>true</code> if this file is deleted
131 */
132 public boolean isDead() {
133 return getLatestRevision().isDead();
134 }
135
136 /**
137 * Returns true, if <code>author</code> worked on this file.
138 * @param author The <code>Author</code> to search for
139 * @return <code>true</code>, if the author is listed in one of
140 * this file's revisions
141 */
142 public boolean hasAuthor(final Author author) {
143 return authors.contains(author);
144 }
145
146 /**
147 * Returns the revision which was replaced by the revision given as
148 * argument. Returns <tt>null</tt> if the given revision is the initial
149 * revision of this file.
150 * @param revision a revision of this file
151 * @return this revision's predecessor
152 */
153 public Revision getPreviousRevision(final Revision revision) {
154 if (!revisions.contains(revision)) {
155 throw new IllegalArgumentException("revision not containted in file");
156 }
157 final SortedSet headSet = revisions.headSet(revision);
158 if (headSet.isEmpty()) {
159 return null;
160 }
161 return (Revision) headSet.last();
162 }
163
164 /**
165 * {@inheritDoc}
166 */
167 public String toString() {
168 return getFilenameWithPath() + " (" + revisions.size() + " revisions)";
169 }
170
171 /**
172 * Compares this file to another one, based on filename.
173 * @see java.lang.Comparable#compareTo(java.lang.Object)
174 */
175 public int compareTo(final Object other) {
176 return filename.compareTo(((VersionedFile) other).filename);
177 }
178
179 /**
180 * Adds an initial revision to the file. An initial revision is either
181 * the first revision of the file, or a re-add after the file was
182 * deleted.
183 * @param revisionNumber the revision number, for example "1.1"
184 * @param author the login from which the change was committed
185 * @param date the time when the change was committed
186 * @param comment the commit message
187 * @param lines the number of lines of the new file
188 */
189 public Revision addInitialRevision(final String revisionNumber, final Author author, final Date date, final String comment, final int lines,
190 final SortedSet symbolicNames) {
191 final Revision result = new Revision(this, revisionNumber, Revision.TYPE_CREATION, author, date, comment, lines, lines, 0, symbolicNames);
192 addRevision(result);
193 return result;
194 }
195
196 /**
197 * Adds a change revision to the file.
198 * @param revisionNumber the revision number, for example "1.1"
199 * @param author the login from which the change was committed
200 * @param date the time when the change was committed
201 * @param comment the commit message
202 * @param lines the number of lines in the file after the change
203 * @param linesDelta the change in the number of lines
204 * @param replacedLines number of lines that were removed and replaced by others
205 */
206 public Revision addChangeRevision(final String revisionNumber, final Author author, final Date date, final String comment, final int lines,
207 final int linesDelta, final int replacedLines, final SortedSet symbolicNames) {
208 final Revision result = new Revision(this, revisionNumber, Revision.TYPE_CHANGE, author, date, comment, lines, linesDelta, replacedLines, symbolicNames);
209 addRevision(result);
210 return result;
211 }
212
213 /**
214 * Adds a deletion revision to the file.
215 * @param revisionNumber the revision number, for example "1.1"
216 * @param author the login from which the change was committed
217 * @param date the time when the change was committed
218 * @param comment the commit message
219 * @param lines the number of lines in the file before it was deleted
220 */
221 public Revision addDeletionRevision(final String revisionNumber, final Author author, final Date date, final String comment, final int lines,
222 final SortedSet symbolicNames) {
223 final Revision result = new Revision(this, revisionNumber, Revision.TYPE_DELETION, author, date, comment, 0, -lines, 0, symbolicNames);
224 addRevision(result);
225 return result;
226 }
227
228 /**
229 * Adds a "begin of log" revision to the file. This kind of revision
230 * only marks the beginning of the log timespan if the file was
231 * already present in the repository at this time. It is not an actual
232 * revision committed by an author.
233 * @param date the begin of the log
234 * @param lines the number of lines in the file at that time
235 */
236 public Revision addBeginOfLogRevision(final Date date, final int lines, final SortedSet symbolicNames) {
237 final Revision result = new Revision(this, "0.0", Revision.TYPE_BEGIN_OF_LOG, null, date, null, lines, 0, 0, symbolicNames);
238 addRevision(result);
239 return result;
240 }
241
242 private void addRevision(final Revision revision) {
243 revisions.add(revision);
244 if (revision.getAuthor() != null) {
245 authors.add(revision.getAuthor());
246 }
247 }
248 }