View Javadoc

1   /*
2    StatSVN - SVN Subversion statistics generation 
3    Copyright (C) 2006 Benoit Xhenseval
4    http://www.statsvn.org
5    
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10  
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Lesser General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with this library; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   
20  */
21  package net.sf.statcvs.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.pages.NavigationNode;
37  import net.sf.statcvs.pages.Page;
38  import net.sf.statcvs.reports.LOCSeriesBuilder;
39  
40  import org.jfree.data.time.Day;
41  import org.jfree.data.time.TimeSeries;
42  
43  /**
44   * A LOC and Churn Chart shows both the LOC and the number of lines touched per
45   * day, this allows you to see the evolution of lines of code and the amount of
46   * changes. A flat LOC with a lot of Churn implies a lot of refactoring, an
47   * increase in LOC in line with churn implies new functionality.
48   * 
49   * @author Benoit Xhenseval (www.ObjectLab.co.uk)
50   */
51  public class ChurnPageMaker {
52      private final ReportConfig config;
53  
54      /**
55       * @see net.sf.statcvs.output.HTMLPage#HTMLPage(Repository)
56       */
57      public ChurnPageMaker(final ReportConfig config) {
58          this.config = config;
59      }
60  
61      public NavigationNode toFile() {
62          final Page page = this.config.createPage("churn", Messages.getString("CHURN_TITLE"), Messages.getString("CHURN_TITLE"));
63          page.addRawContent("\n\n<!-- The LOC and Churn Report was designed by Benoit Xhenseval (http://www.objectlab.co.uk/open)-->");
64          page.addRawContent("\n<!-- Initially part of StatSVN -->\n\n");
65          page.addRawContent("<p>" + Messages.getString("CHURN_DESCRIPTION") + "</p>");
66          page.add(buildChart());
67          return page;
68      }
69  
70      private ChartImage buildChart() {
71          final Map changePerRevision = new HashMap();
72          final SortedSet revisions = config.getRepository().getRevisions();
73          for (final Iterator it = revisions.iterator(); it.hasNext();) {
74              final Revision rev = (Revision) it.next();
75              final Date dateToUse = blastTime(rev.getDate());
76              final Integer changes = (Integer) changePerRevision.get(dateToUse);
77              if (changes == null) {
78                  changePerRevision.put(dateToUse, new Integer(Math.abs(getLineChanges(rev))));
79              } else {
80                  changePerRevision.put(dateToUse, new Integer(Math.abs(changes.intValue()) + getLineChanges(rev)));
81              }
82          }
83  
84          final List annotations = SymbolicNameAnnotation.createAnnotations(config.getRepository().getSymbolicNames());
85          final TimeSeries timeLine = new TimeSeries(Messages.getString("CHURN_TOUCHED_LINE"), Day.class);
86  
87          for (final Iterator it = changePerRevision.entrySet().iterator(); it.hasNext();) {
88              final Map.Entry entry = (Entry) it.next();
89  
90              //			SvnConfigurationOptions.getTaskLogger().log("Churn on " + entry.getKey() + " ==> " + entry.getValue());
91              timeLine.add(new Day((Date) entry.getKey()), ((Integer) entry.getValue()).intValue());
92          }
93  
94          final TimeSeries locSeries = getLOCTimeSeries(revisions, Messages.getString("TIME_LOC_SUBTITLE"));
95  
96          final LOCChurnChartMaker chart = new LOCChurnChartMaker("locandchurn", config, timeLine, locSeries, Messages.getString("LOC_CHURN_CHART_TITLE"),
97                  "locandchurn.png", annotations);
98          return chart.toFile();
99      }
100 
101     private Date blastTime(final Date date) {
102         final Calendar cal = Calendar.getInstance();
103         cal.setTime(date);
104         cal.set(Calendar.MILLISECOND, 0);
105         cal.set(Calendar.HOUR_OF_DAY, 0);
106         cal.set(Calendar.MINUTE, 0);
107         cal.set(Calendar.SECOND, 0);
108         return cal.getTime();
109     }
110 
111     private int getLineChanges(final Revision rev) {
112         if (rev.isDead()) {
113             return rev.getLinesDelta();
114         }
115         return Math.abs(rev.getLinesDelta()) + 2 * rev.getReplacedLines();
116     }
117 
118     private TimeSeries getLOCTimeSeries(final SortedSet revisions, final String title) {
119         final Iterator it = revisions.iterator();
120         final LOCSeriesBuilder locCounter = new LOCSeriesBuilder(title, true);
121         while (it.hasNext()) {
122             locCounter.addRevision((Revision) it.next());
123         }
124         return locCounter.getTimeSeries();
125     }
126 }