1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package net.sf.statsvn.output;
22
23 import java.util.Calendar;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.SortedSet;
30 import java.util.Map.Entry;
31
32 import net.sf.statcvs.Messages;
33 import net.sf.statcvs.charts.ChartImage;
34 import net.sf.statcvs.charts.SymbolicNameAnnotation;
35 import net.sf.statcvs.model.Revision;
36 import net.sf.statcvs.output.ReportConfig;
37 import net.sf.statcvs.pages.NavigationNode;
38 import net.sf.statcvs.pages.Page;
39 import net.sf.statcvs.reports.LOCSeriesBuilder;
40
41 import org.jfree.data.time.Day;
42 import org.jfree.data.time.TimeSeries;
43
44 /**
45 * A LOC and Churn Chart shows both the LOC and the number of lines touched per
46 * day, this allows you to see the evolution of lines of code and the amount of
47 * changes. A flat LOC with a lot of Churn implies a lot of refactoring, an
48 * increase in LOC in line with churn implies new functionality.
49 *
50 * @author Benoit Xhenseval (www.ObjectLab.co.uk)
51 */
52 public class ChurnPageMaker {
53 private final ReportConfig config;
54
55 /**
56 * @see net.sf.statcvs.output.HTMLPage#HTMLPage(Repository)
57 */
58 public ChurnPageMaker(final ReportConfig config) {
59 this.config = config;
60 }
61
62 public NavigationNode toFile() {
63 final Page page = this.config.createPage("churn", Messages.getString("CHURN_TITLE"), Messages.getString("CHURN_TITLE"));
64 page.addRawContent("\n\n<!-- The LOC and Churn Report was designed by Benoit Xhenseval (http://www.objectlab.co.uk/open)-->");
65 page.addRawContent("\n<!-- Initially part of StatSVN -->\n\n");
66 page.addRawContent("<p>" + Messages.getString("CHURN_DESCRIPTION") + "</p>");
67 page.add(buildChart());
68 return page;
69 }
70
71 private ChartImage buildChart() {
72 Map changePerRevision = new HashMap();
73 SortedSet revisions = config.getRepository().getRevisions();
74 for (Iterator it = revisions.iterator(); it.hasNext();) {
75 Revision rev = (Revision) it.next();
76 Date dateToUse = blastTime(rev.getDate());
77 Integer changes = (Integer) changePerRevision.get(dateToUse);
78 if (changes == null) {
79 changePerRevision.put(dateToUse, new Integer(Math.abs(getLineChanges(rev))));
80 } else {
81 changePerRevision.put(dateToUse, new Integer(Math.abs(changes.intValue()) + getLineChanges(rev)));
82 }
83 }
84
85 List annotations = SymbolicNameAnnotation.createAnnotations(config.getRepository().getSymbolicNames());
86 TimeSeries timeLine = new TimeSeries(Messages.getString("CHURN_TOUCHED_LINE"), Day.class);
87
88 for (Iterator it = changePerRevision.entrySet().iterator(); it.hasNext();) {
89 Map.Entry entry = (Entry) it.next();
90
91 SvnConfigurationOptions.getTaskLogger().log("Churn on " + entry.getKey() + " ==> " + entry.getValue());
92 timeLine.add(new Day((Date) entry.getKey()), ((Integer) entry.getValue()).intValue());
93 }
94
95 TimeSeries locSeries = getLOCTimeSeries(revisions, Messages.getString("TIME_LOC_SUBTITLE"));
96
97 LOCChurnChartMaker chart = new LOCChurnChartMaker(config, timeLine, locSeries, Messages.getString("LOC_CHURN_CHART_TITLE"), "locandchurn.png", config
98 .getLargeChartSize(), annotations);
99 return chart.toFile();
100 }
101
102 private Date blastTime(final Date date) {
103 Calendar cal = Calendar.getInstance();
104 cal.setTime(date);
105 cal.set(Calendar.MILLISECOND, 0);
106 cal.set(Calendar.HOUR_OF_DAY, 0);
107 cal.set(Calendar.MINUTE, 0);
108 cal.set(Calendar.SECOND, 0);
109 return cal.getTime();
110 }
111
112 private int getLineChanges(Revision rev) {
113 if (rev.isDead()) {
114 return rev.getLinesDelta();
115 }
116 return Math.abs(rev.getLinesDelta()) + 2 * rev.getReplacedLines();
117 }
118
119 private TimeSeries getLOCTimeSeries(final SortedSet revisions, final String title) {
120 final Iterator it = revisions.iterator();
121 final LOCSeriesBuilder locCounter = new LOCSeriesBuilder(title, true);
122 while (it.hasNext()) {
123 locCounter.addRevision((Revision) it.next());
124 }
125 return locCounter.getTimeSeries();
126 }
127 }