1 package net.sf.statcvs.pages;
2
3 import java.text.NumberFormat;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.Iterator;
8 import java.util.List;
9
10 import net.sf.statcvs.Messages;
11 import net.sf.statcvs.charts.ChartImage;
12 import net.sf.statcvs.charts.DirectoryPieChartMaker.CodeDistributionChartMaker;
13 import net.sf.statcvs.charts.TimeBarChartMaker.HourBarChartMaker;
14 import net.sf.statcvs.charts.TimeBarChartMaker.WeekdayBarChartMaker;
15 import net.sf.statcvs.model.Author;
16 import net.sf.statcvs.model.Commit;
17 import net.sf.statcvs.model.Repository;
18 import net.sf.statcvs.model.Revision;
19 import net.sf.statcvs.output.ConfigurationOptions;
20 import net.sf.statcvs.output.ReportConfig;
21 import net.sf.statcvs.reports.DirectoriesForAuthorTableReport;
22 import net.sf.statcvs.util.StringUtils;
23
24 /**
25 * @author anja
26 * @author Richard Cyganiak (richard@cyganiak.de)
27 * @version $Id: DeveloperPageMaker.java,v 1.18 2009/08/20 08:57:00 benoitx Exp $
28 */
29 public class DeveloperPageMaker {
30 private final static int RECENT_COMMITS_LENGTH = 20;
31 private final static NumberFormat PERCENT_FORMAT = NumberFormat.getPercentInstance();
32
33 static {
34 PERCENT_FORMAT.setMinimumFractionDigits(1);
35 PERCENT_FORMAT.setMaximumFractionDigits(1);
36 }
37
38 public static String getURL(final Author developer) {
39 return getFilename(developer) + ".html";
40 }
41
42 private static String getFilename(final Author developer) {
43 return "user_" + HTML.escapeAuthorName(developer.getName());
44 }
45
46 private final ReportConfig config;
47 private final Author developer;
48 private final Repository repository;
49
50 public DeveloperPageMaker(final ReportConfig config, final Author developer) {
51 this.config = config;
52 this.developer = developer;
53 this.repository = config.getRepository();
54 }
55
56 public Page toFile() {
57 final ChartImage hourChart = new HourBarChartMaker("activity_time", this.config, this.developer.getRevisions(), Messages
58 .getString("ACTIVITY_TIME_FOR_AUTHOR_TITLE")
59 + " " + this.developer.getRealName(), "activity_time_" + HTML.escapeAuthorName(this.developer.getName()) + ".png").toFile();
60 final ChartImage weekdayChart = new WeekdayBarChartMaker("activity_day", this.config, this.developer.getRevisions(), Messages
61 .getString("ACTIVITY_DAY_FOR_AUTHOR_TITLE")
62 + " " + this.developer.getRealName(), "activity_day_" + HTML.escapeAuthorName(this.developer.getName()) + ".png").toFile();
63 final ChartImage codeDistributionChart = new CodeDistributionChartMaker(this.config, this.developer).toFile();
64
65 String title;
66 if (this.config.isDeveloper(this.developer)) {
67 title = this.config.getProjectName() + " Developers: " + this.developer.getRealName();
68 } else {
69 title = "Non-developer Login: " + this.developer.getRealName();
70 }
71 final Page page = this.config.createPage(getFilename(this.developer), this.developer.getRealName(), title);
72 page.addAttribute("Login name", this.developer.getName());
73 if (this.developer.getRealName() != null && !this.developer.getRealName().equals(this.developer.getName())) {
74 page.addAttribute("Real name", this.developer.getRealName());
75 }
76 if (StringUtils.isNotEmpty(this.developer.getTwitterUserName())) {
77 page.addRawAttribute("Twitter", "@<a href=\"http://twitter.com/" + this.developer.getTwitterUserName() + "\">"
78 + this.developer.getTwitterUserName() + "</a>");
79 }
80 if (StringUtils.isNotEmpty(this.developer.getEmail())) {
81 page.addRawAttribute("Email", "<a href=\"mailto:" + this.developer.getEmail() + "\">" + this.developer.getEmail() + "</a>");
82 }
83 if (StringUtils.isNotEmpty(this.developer.getHomePageUrl())) {
84 page.addRawAttribute("Home Page", "<a href=\"" + this.developer.getHomePageUrl() + "\">" + this.developer.getHomePageUrl() + "</a>");
85 }
86 if (StringUtils.isNotEmpty(this.developer.getImageUrl())) {
87 page.addRawAttribute("Image", "<img src=\"" + this.developer.getImageUrl() + "\" alt=\"" + this.developer.getRealName() + "\"/>");
88 }
89
90 page.addAttribute("Total Commits", getNumberAndPercentage(this.developer.getRevisions().size(), this.repository.getRevisions().size()));
91 String loc = getNumberAndPercentage(countContributedLines(this.developer.getRevisions()), countContributedLines(this.repository.getRevisions()));
92 page.addAttribute("Lines of Code", loc);
93 page.addAttribute("Most Recent Commit", ((Revision) this.developer.getRevisions().last()).getDate());
94 if (ConfigurationOptions.isEnableTwitterButton()) {
95 page.addRawAttribute("Tweet this", TwitterHelp.buildDeveloperLink(developer, loc, repository, config));
96 }
97 page.addSection(Messages.getString("ACTIVITY_TITLE"));
98 page.add(hourChart);
99 page.add(weekdayChart);
100
101 if (StringUtils.isNotEmpty(this.developer.getTwitterUserName()) && this.developer.isTwitterIncludeHtml()) {
102 page.addSection("Twitter");
103 page.addRawContent("<div id=\"twitter_div\">");
104 page.addRawContent("<ul id=\"twitter_update_list\"/>");
105 page.addRawContent("<a href=\"http://twitter.com/" + developer.getTwitterUserName()
106 + "\" id=\"twitter-link\" style=\"display:block;text-align:right;\">follow me on Twitter</a>");
107 page.addRawContent("</div>");
108 page.addRawContent("<script type=\"text/javascript\" src=\"http://twitter.com/javascripts/blogger.js\"></script>");
109 page.addRawContent("<script type=\"text/javascript\" src=\"http://twitter.com/statuses/user_timeline/" + developer.getTwitterUserName()
110 + ".json?callback=twitterCallback2&count=5\"></script>");
111 }
112 if (StringUtils.isNotEmpty(this.developer.getTwitterUserId()) && this.developer.isTwitterIncludeFlash()) {
113 page.addSection("Twitter");
114 page
115 .addRawContent("<object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,124,0\" width=\"550\" height=\"350\" id=\"TwitterWidget\" align=\"middle\">");
116 page.addRawContent("<param name=\"allowScriptAccess\" value=\"sameDomain\" />");
117 page.addRawContent("<param name=\"allowFullScreen\" value=\"false\" />");
118 page.addRawContent("<param name=\"movie\" value=\"http://static.twitter.com/flash/widgets/profile/TwitterWidget.swf\" />");
119 page.addRawContent("<param name=\"quality\" value=\"high\" />");
120 page.addRawContent("<param name=\"bgcolor\" value=\"#000000\" />");
121 page.addRawContent("<param name=\"FlashVars\" value=\"userID=" + developer.getTwitterUserId()
122 + "&styleURL=http://static.twitter.com/flash/widgets/profile/velvetica.xml\"/>");
123 page
124 .addRawContent("<embed src=\"http://static.twitter.com/flash/widgets/profile/TwitterWidget.swf\" quality=\"high\" bgcolor=\"#000000\" width=\"550\" height=\"350\" name=\"TwitterWidget\" align=\"middle\" allowScriptAccess=\"sameDomain\" allowFullScreen=\"false\" type=\"application/x-shockwave-flash\" pluginspage=\"http://www.macromedia.com/go/getflashplayer\" FlashVars=\"userID="
125 + developer.getTwitterUserId() + "&styleURL=http://static.twitter.com/flash/widgets/profile/velvetica.xml\"/></object>");
126 }
127
128 page.addSection("Activity in Directories");
129 page.add(new DirectoriesForAuthorTableReport(this.config, this.developer));
130 if (codeDistributionChart != null) {
131 page.add(codeDistributionChart);
132 }
133 page.addSection(Messages.getString("MOST_RECENT_COMMITS"));
134 page.addRawContent(new CommitListFormatter(this.config, getRecentCommits(), Collections.EMPTY_LIST, RECENT_COMMITS_LENGTH, false).render());
135 return page;
136 }
137
138 private List getRecentCommits() {
139 final List results = new ArrayList();
140 final Iterator it = this.repository.getCommits().iterator();
141 while (it.hasNext()) {
142 final Commit commit = (Commit) it.next();
143 if (this.developer.equals(commit.getAuthor())) {
144 results.add(commit);
145 }
146 }
147 return results;
148 }
149
150 private int countContributedLines(final Collection revisions) {
151 int result = 0;
152 final Iterator it = revisions.iterator();
153 while (it.hasNext()) {
154 final Revision element = (Revision) it.next();
155 result += element.getNewLines();
156 }
157 return result;
158 }
159
160 /**
161 * returns the percentage of a given total count and the count.
162 * This will work, because division by zero is not a problem with doubles:
163 * you get NaN (and the formatter will format that too).
164 * @author Jan Dockx
165 * @param value
166 * @param total
167 * @return String percentage string
168 */
169 private String getNumberAndPercentage(final int value, final int total) {
170 final double factor = (double) value / (double) total;
171 return NumberFormat.getNumberInstance().format(value) + " (" + PERCENT_FORMAT.format(factor) + ")";
172 }
173 }