1 package net.sf.statcvs.pages;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.HashSet;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Set;
10
11 import net.sf.statcvs.Messages;
12 import net.sf.statcvs.model.Commit;
13 import net.sf.statcvs.model.Revision;
14 import net.sf.statcvs.model.SymbolicName;
15 import net.sf.statcvs.output.ReportConfig;
16 import net.sf.statcvs.output.WebRepositoryIntegration;
17 import net.sf.statcvs.renderer.FileCollectionFormatter;
18
19 /**
20 * Class for formatting a list of commits as HTML.
21 *
22 * @author Anja Jentzsch
23 * @author Richard Cyganiak (richard@cyganiak.de)
24 * @version $Id: CommitListFormatter.java,v 1.17 2009/08/22 10:30:42 benoitx Exp $
25 */
26 public class CommitListFormatter {
27 private final ReportConfig config;
28 private final List commits;
29 private final List tags;
30 private final int max;
31 private final boolean withPermalinks;
32 private final HashMap commitHashMap = new HashMap();
33
34 /**
35 * Creates a new instance for the list of commits.
36 * @param commits A list of {@link Commit} objects
37 */
38 public CommitListFormatter(final ReportConfig config, final List commits, final List tags, final boolean withPermalinks) {
39 this(config, commits, tags, Integer.MAX_VALUE, withPermalinks);
40 }
41
42 /**
43 * Creates a new instance for the list of commits.
44 * @param commits A list of {@link Commit} objects
45 * @param max maximum number of commits for the log; if there
46 * are more, only the most recent will be used
47 */
48 public CommitListFormatter(final ReportConfig config, final List commit, final List tags, final int max, final boolean withPermalinks) {
49 this.config = config;
50 this.commits = new ArrayList(commit);
51 this.tags = tags;
52 this.max = max;
53 this.withPermalinks = withPermalinks;
54 Collections.reverse(this.commits);
55 }
56
57 /**
58 * Returns HTML code for the commit log without splitting the list
59 * into pages.
60 *
61 * @return HTML code for the commit log
62 */
63 public String render() {
64 if (this.commits.size() > this.max) {
65 final List recentCommits = this.commits.subList(0, this.max);
66 return renderCommitList(recentCommits) + "<p>(" + (this.commits.size() - this.max) + " " + Messages.getString("MORE_COMMITS") + ")</p>\n";
67 }
68 return renderCommitList(this.commits);
69 }
70
71 private String renderCommitList(final List commitList) {
72 if (commitList.isEmpty()) {
73 return "<p>No commits</p>\n";
74 }
75 int id = commitList.size();
76 final StringBuffer result = new StringBuffer("<dl class=\"commitlist\">\n");
77 final Iterator commitIt = commitList.iterator();
78 Commit nextCommit = commitIt.hasNext() ? (Commit) commitIt.next() : null;
79 final Iterator tagIt = this.tags.iterator();
80 SymbolicName nextTag = tagIt.hasNext() ? (SymbolicName) tagIt.next() : null;
81 while (nextCommit != null) {
82 if (nextTag == null || nextCommit.getDate().getTime() > nextTag.getDate().getTime()) {
83 result.append(renderCommit(nextCommit, id));
84 nextCommit = commitIt.hasNext() ? (Commit) commitIt.next() : null;
85 id--;
86 } else {
87 renderTag(result, nextTag);
88 nextTag = tagIt.hasNext() ? (SymbolicName) tagIt.next() : null;
89 }
90 }
91 result.append("</dl>\n\n");
92 return result.toString();
93 }
94
95 private void renderTag(final StringBuffer s, final SymbolicName tag) {
96 final String anchor = HTML.escape(CommitLogPageMaker.getAnchor(tag));
97 s.append(" <dt class=\"tag\"><a name=\"").append(anchor).append("\">\n");
98 s.append(" Repository Tag: ").append(HTML.escape(tag.getName())).append("</a>\n");
99 s.append(" </dt>\n");
100 }
101
102 private String renderCommit(final Commit commit, final int id) {
103 final StringBuffer result = new StringBuffer();
104 result.append(" <dt><a name=\"" + id + "\"></a>\n");
105 result.append(" ").append(getAuthor(commit)).append("\n");
106 result.append(" ").append(getDate(commit)).append("\n");
107 if (this.withPermalinks) {
108 result.append(" ").append(getPermalink(id)).append("\n");
109 }
110 final String revisionNumber = getRevisionNumber(commit);
111 if (revisionNumber != null) {
112 result.append(" ").append(revisionNumber).append("\n");
113 }
114 result.append(" </dt>\n");
115 result.append(" <dd>\n");
116 result.append(" <p class=\"comment\">\n");
117 result.append(getComment(commit)).append("\n");
118 result.append(" </p>\n");
119 result.append(" <p class=\"commitdetails\"><strong>");
120 result.append(getLinesOfCode(commit)).append("</strong> ");
121 result.append("lines of code changed in ");
122 result.append(getAffectedFilesCount(commit));
123 result.append(":</p>\n");
124 result.append(getAffectedFiles(commit)).append(" </dd>\n\n");
125 return result.toString();
126 }
127
128 private String getPermalink(final int id) {
129 return "<a class=\"permalink\" title=\"Permalink to this commit\" href=\"#" + id + "\">#" + id + "</a>";
130 }
131
132 private String getRevisionNumber(final Commit commit) {
133 final Set rev = new HashSet();
134 for (final Iterator it = commit.getRevisions().iterator(); it.hasNext();) {
135 rev.add(((Revision) it.next()).getRevisionNumber());
136 }
137 if (rev.size() == 1) {
138 return HTML.getRevisionNumber((String) rev.iterator().next());
139 } else {
140 return null;
141 }
142 }
143
144 private String getDate(final Commit commit) {
145 return HTML.getDateAndTime(commit.getDate());
146 }
147
148 private String getAuthor(final Commit commit) {
149 return HTML.getAuthorLink(commit.getAuthor());
150 }
151
152 private String getAffectedFilesCount(final Commit commit) {
153 return HTML.getAffectedFilesCount(commit.getRevisions());
154 }
155
156 private String getComment(final Commit commit) {
157 return this.config.getWebBugtracker().toHTMLWithLinks(commit.getComment());
158 }
159
160 private String getLinesOfCode(final Commit commit) {
161 final Iterator it = commit.getRevisions().iterator();
162 int locSum = 0;
163 while (it.hasNext()) {
164 final Revision each = (Revision) it.next();
165 locSum += each.getNewLines();
166 saveRevision(each);
167 }
168 return Integer.toString(locSum);
169 }
170
171 private void saveRevision(final Revision revision) {
172 commitHashMap.put(revision.getFile().getFilenameWithPath(), revision);
173 }
174
175 private String getAffectedFiles(final Commit commit) {
176 final StringBuffer result = new StringBuffer(" <ul class=\"commitdetails\">\n");
177 final FileCollectionFormatter formatter = new FileCollectionFormatter(commit.getAffectedFiles());
178 final Iterator it = formatter.getDirectories().iterator();
179 while (it.hasNext()) {
180 result.append(" <li>\n");
181 final String directory = (String) it.next();
182 if (!directory.equals("")) {
183 result.append(" <strong>").append(directory.substring(0, directory.length() - 1)).append("</strong>:\n");
184 }
185 final Iterator files = formatter.getFiles(directory).iterator();
186 final StringBuffer fileList = new StringBuffer();
187 while (files.hasNext()) {
188 if (fileList.length() > 0) {
189 fileList.append(",\n");
190 }
191 fileList.append(" ");
192 final String file = (String) files.next();
193 final Revision revision = (Revision) commitHashMap.get(directory + file);
194 final WebRepositoryIntegration webRepository = this.config.getWebRepository();
195 if (webRepository != null) {
196 final Revision previous = revision.getPreviousRevision();
197 String url;
198 if (previous == null || revision.isInitialRevision()) {
199 url = webRepository.getFileViewUrl(revision);
200 } else if (revision.isDead()) {
201 url = webRepository.getFileViewUrl(previous);
202 } else {
203 url = webRepository.getDiffUrl(previous, revision);
204 }
205 fileList.append("<a href=\"").append(HTML.escapeUrl(url)).append("\" class=\"webrepository\">").append(HTML.escape(file)).append("</a>");
206 } else {
207 fileList.append(file);
208 }
209 if (revision.isInitialRevision()) {
210 final int linesAdded = revision.getLines();
211 fileList.append(" <span class=\"new\">(new");
212 if (linesAdded > 0) {
213 fileList.append(" ").append(linesAdded);
214 }
215 fileList.append(")</span>");
216 } else if (revision.isDead()) {
217 fileList.append(" <span class=\"del\">(del)</span>");
218 } else {
219 final int delta = revision.getLinesDelta();
220 final int linesAdded = revision.getReplacedLines() + ((delta > 0) ? delta : 0);
221 final int linesRemoved = revision.getReplacedLines() - ((delta < 0) ? delta : 0);
222 fileList.append(" <span class=\"change\">(");
223 if (linesAdded > 0) {
224 fileList.append("+").append(linesAdded);
225 if (linesRemoved > 0) {
226 fileList.append(" -").append(linesRemoved);
227 }
228 } else if (linesRemoved > 0) {
229 fileList.append("-").append(linesRemoved);
230 } else {
231
232 fileList.append("changed");
233 }
234 fileList.append(")</span>");
235 }
236 }
237 result.append(fileList.toString()).append("\n </li>\n");
238 }
239 result.append(" </ul>\n");
240 return result.toString();
241 }
242 }