View Javadoc

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("&#160;<span class=\"new\">(new");
212                     if (linesAdded > 0) {
213                         fileList.append("&#160;").append(linesAdded);
214                     }
215                     fileList.append(")</span>");
216                 } else if (revision.isDead()) {
217                     fileList.append("&#160;<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("&#160;<span class=\"change\">(");
223                     if (linesAdded > 0) {
224                         fileList.append("+").append(linesAdded);
225                         if (linesRemoved > 0) {
226                             fileList.append("&#160;-").append(linesRemoved);
227                         }
228                     } else if (linesRemoved > 0) {
229                         fileList.append("-").append(linesRemoved);
230                     } else { // linesAdded == linesRemoved == 0
231                         // should be binary file or keyword subst change
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 }