View Javadoc

1   package net.sf.statcvs.charts;
2   
3   import java.awt.Color;
4   import java.awt.Dimension;
5   import java.awt.Paint;
6   import java.util.ArrayList;
7   import java.util.Collection;
8   import java.util.Collections;
9   import java.util.Comparator;
10  import java.util.Date;
11  import java.util.HashMap;
12  import java.util.Iterator;
13  import java.util.List;
14  import java.util.Map;
15  import java.util.SortedSet;
16  
17  import net.sf.statcvs.Messages;
18  import net.sf.statcvs.model.Author;
19  import net.sf.statcvs.model.Directory;
20  import net.sf.statcvs.model.Repository;
21  import net.sf.statcvs.model.Revision;
22  import net.sf.statcvs.output.ReportConfig;
23  import net.sf.statcvs.pages.HTML;
24  import net.sf.statcvs.reports.LOCSeriesBuilder;
25  import net.sf.statcvs.util.IntegerMap;
26  
27  import org.jfree.chart.ChartFactory;
28  import org.jfree.chart.JFreeChart;
29  import org.jfree.chart.annotations.XYAnnotation;
30  import org.jfree.chart.axis.DateAxis;
31  import org.jfree.chart.axis.ValueAxis;
32  import org.jfree.chart.plot.XYPlot;
33  import org.jfree.chart.renderer.xy.XYStepRenderer;
34  import org.jfree.data.time.TimeSeries;
35  import org.jfree.data.time.TimeSeriesCollection;
36  
37  /**
38   * Produces Lines Of Code charts
39   * 
40   * TODO: At least the single-series charts should be done by TimeLineChartMakers
41   * 
42   * @author jentzsch
43   * @author Richard Cyganiak (richard@cyganiak.de)
44   * @version $Id: LOCChartMaker.java,v 1.14 2008/04/02 11:22:15 benoitx Exp $
45   */
46  public class LOCChartMaker {
47      private final ReportConfig config;
48      private ChartImage chartFile = null;
49  
50      /**
51       * Creates a Lines Of Code chart from a <tt>BasicTimeSeries</tt> and
52       * saves it as PNG
53       * @param locSeries the LOC history
54       * @param title the chart title
55       * @param fileName the filename where the chart will be saved
56       * @param size width and height of PNG in pixels
57       * @param annotations
58       */
59      public LOCChartMaker(final ReportConfig config, final TimeSeries locSeries, final String title, final String fileName, final Dimension size,
60              final List annotations) {
61          this.config = config;
62          if (locSeries == null) {
63              return;
64          }
65          final Paint[] colors = new Paint[1];
66          colors[0] = Color.RED;
67  
68          final TimeSeriesCollection collection = new TimeSeriesCollection();
69          collection.addSeries(locSeries);
70          final JFreeChart chart = createLOCChart(collection, colors, title, annotations);
71          this.chartFile = this.config.createChartImage(fileName, title, chart, size);
72      }
73  
74      /**
75       * Creates a Lines Of Code chart from a list of <tt>BasicTimesSeries</tt> and
76       * saves it as PNG
77       * @param locSeriesList a list of <tt>BasicTimesSeries</tt>
78       * @param title the chart title
79       * @param fileName the filename where the chart will be saved
80       * @param size width and height of PNG in pixels
81       */
82      public LOCChartMaker(final ReportConfig config, final List locSeriesList, final String title, final String fileName, final Dimension size,
83              final List annotations) {
84          this.config = config;
85          if (locSeriesList.isEmpty()) {
86              return;
87          }
88          int i = 0;
89          final TimeSeriesCollection collection = new TimeSeriesCollection();
90          final Iterator it = locSeriesList.iterator();
91          while (it.hasNext()) {
92              final TimeSeries series = (TimeSeries) it.next();
93              collection.addSeries(series);
94              i++;
95          }
96          final JFreeChart chart = createLOCChart(collection, null, title, annotations);
97          this.chartFile = this.config.createChartImage(fileName, title, chart, size);
98      }
99  
100     private JFreeChart createLOCChart(final TimeSeriesCollection data, final Paint[] colors, final String title, final List annotations) {
101         final String domain = Messages.getString("TIME_LOC_DOMAIN");
102         final String range = Messages.getString("TIME_LOC_RANGE");
103 
104         final boolean legend = (data.getSeriesCount() > 1);
105         final JFreeChart chart = ChartFactory.createTimeSeriesChart(this.config.getProjectName() + ": " + title, domain, range, data, legend, false, false);
106 
107         final XYPlot plot = chart.getXYPlot();
108         plot.setRenderer(new XYStepRenderer());
109         if (colors == null) {
110             // We don't like the bright yellow color early on in the series, use a darker one
111             for (int i = 0; i < plot.getSeriesCount(); i++) {
112                 final Paint seriesPaint = plot.getRenderer().getSeriesPaint(i);
113                 if (seriesPaint != null && seriesPaint.equals(new Color(0xFF, 0xFF, 0x55))) {
114                     plot.getRenderer().setSeriesPaint(i, new Color(240, 220, 0x55));
115                 }
116             }
117         } else {
118             for (int i = 0; i < colors.length; i++) {
119                 plot.getRenderer().setSeriesPaint(i, colors[i]);
120             }
121         }
122         final DateAxis domainAxis = (DateAxis) plot.getDomainAxis();
123         domainAxis.setVerticalTickLabels(true);
124         final ValueAxis valueAxis = plot.getRangeAxis();
125         valueAxis.setLowerBound(0);
126 
127         if (annotations != null) {
128             for (final Iterator it = annotations.iterator(); it.hasNext();) {
129                 plot.addAnnotation((XYAnnotation) it.next());
130             }
131         }
132         return chart;
133     }
134 
135     public ChartImage toFile() {
136         return this.chartFile;
137     }
138 
139     private static TimeSeries getLOCTimeSeries(final SortedSet revisions, final String title) {
140         final LOCSeriesBuilder locCounter = new LOCSeriesBuilder(title, true);
141         final Iterator it = revisions.iterator();
142         while (it.hasNext()) {
143             locCounter.addRevision((Revision) it.next());
144         }
145         if (locCounter.getMaximum() == 0) {
146             return null;
147         }
148         return locCounter.getTimeSeries();
149     }
150 
151     public static class MainLOCChartMaker extends LOCChartMaker {
152         public MainLOCChartMaker(final ReportConfig config, final String fileName, final Dimension size) {
153             super(config, getLOCTimeSeries(config.getRepository().getRevisions(), Messages.getString("TIME_LOC_SUBTITLE")), Messages
154                     .getString("TIME_LOC_SUBTITLE"), fileName, size, SymbolicNameAnnotation.createAnnotations(config.getRepository().getSymbolicNames()));
155         }
156     }
157 
158     public static class DirectoryLOCChartMaker extends LOCChartMaker {
159         private static String getTitle(final Directory directory) {
160             return directory.getPath() + (directory.getPath() != null && directory.getPath().length() > 1 ? " " : "") + Messages.getString("TIME_LOC_SUBTITLE");
161         }
162 
163         private static String getFilename(final Directory directory) {
164             return "loc_module" + HTML.escapeDirectoryName(directory.getPath()) + ".png";
165         }
166 
167         public DirectoryLOCChartMaker(final ReportConfig config, final Directory directory) {
168             super(config, getLOCTimeSeries(directory.getRevisions(), getTitle(directory)), getTitle(directory), getFilename(directory), config
169                     .getLargeChartSize(), SymbolicNameAnnotation.createAnnotations(config.getRepository().getSymbolicNames()));
170         }
171     }
172 
173     public static class AllDevelopersLOCChartMaker extends LOCChartMaker {
174         private static List createAllDevelopersLOCSeries(final ReportConfig config) {
175             Iterator it = config.getRepository().getAuthors().iterator();
176             final Map authorSeriesMap = new HashMap();
177             while (it.hasNext()) {
178                 final Author author = (Author) it.next();
179                 if (!config.isDeveloper(author)) {
180                     continue;
181                 }
182                 authorSeriesMap.put(author, new LOCSeriesBuilder(author.getRealName(), false));
183             }
184             it = config.getRepository().getRevisions().iterator();
185             while (it.hasNext()) {
186                 final Revision rev = (Revision) it.next();
187                 if (rev.isBeginOfLog()) {
188                     continue;
189                 }
190                 final LOCSeriesBuilder builder = (LOCSeriesBuilder) authorSeriesMap.get(rev.getAuthor());
191                 if (builder != null) {
192                     builder.addRevision(rev);
193                 } // otherwise the revision was by a non-developer login
194             }
195             final List authors = new ArrayList(authorSeriesMap.keySet());
196             Collections.sort(authors);
197             final List result = new ArrayList();
198             it = authors.iterator();
199             while (it.hasNext()) {
200                 final Author author = (Author) it.next();
201                 final LOCSeriesBuilder builder = (LOCSeriesBuilder) authorSeriesMap.get(author);
202                 final TimeSeries series = builder.getTimeSeries();
203                 if (series != null) {
204                     result.add(series);
205                 }
206             }
207             return result;
208         }
209 
210         public AllDevelopersLOCChartMaker(final ReportConfig config, final Dimension size) {
211             super(config, createAllDevelopersLOCSeries(config), "Contributed Lines of Code", "loc_per_author.png", size, SymbolicNameAnnotation
212                     .createAnnotations(config.getRepository().getSymbolicNames()));
213         }
214     }
215 
216     public static class AllDirectoriesLOCChartMaker extends LOCChartMaker {
217         private static Collection getMajorDirectories(final Repository repository, final int max) {
218             if (repository.getFirstDate() == null || repository.getLastDate() == null || repository.getFirstDate().equals(repository.getLastDate())) {
219                 return Collections.EMPTY_LIST;
220             }
221             final IntegerMap importances = new IntegerMap();
222             final Iterator it = repository.getDirectories().iterator();
223             while (it.hasNext()) {
224                 final Directory directory = (Directory) it.next();
225                 importances.put(directory, getImportance(directory, repository.getFirstDate(), repository.getLastDate()));
226             }
227             final List result = new ArrayList(repository.getDirectories());
228             Collections.sort(result, new Comparator() {
229                 public int compare(final Object o1, final Object o2) {
230                     final int importance1 = importances.get(o1);
231                     final int importance2 = importances.get(o2);
232                     if (importance1 > importance2) {
233                         return -1;
234                     }
235                     if (importance1 == importance2) {
236                         return 0;
237                     }
238                     return 1;
239                 }
240             });
241             return firstN(result, max);
242         }
243 
244         private static int getImportance(final Directory dir, final Date start, final Date end) {
245             final long timeRange = end.getTime() - start.getTime();
246             double maxImportance = 0;
247             int currentLines = 0;
248             final Iterator it = dir.getRevisions().iterator();
249             while (it.hasNext()) {
250                 final Revision revision = (Revision) it.next();
251                 currentLines += revision.getLinesDelta();
252                 final long timeInRange = revision.getDate().getTime() - start.getTime();
253                 final double timeFraction = (timeInRange / timeRange) * 0.9 + 0.1;
254                 maxImportance = Math.max(maxImportance, (currentLines) * (timeFraction));
255             }
256             return (int) (maxImportance * 10);
257         }
258 
259         private static List firstN(final List list, final int n) {
260             return list.subList(0, Math.min(list.size(), n));
261         }
262 
263         private static List createAllDirectoriesLOCSeries(final Repository repository, final int max) {
264             Iterator it = getMajorDirectories(repository, max).iterator();
265             final Map directorySeriesMap = new HashMap();
266             while (it.hasNext()) {
267                 final Directory directory = (Directory) it.next();
268                 directorySeriesMap.put(directory, new LOCSeriesBuilder(directory.getPath(), true));
269             }
270             it = repository.getRevisions().iterator();
271             while (it.hasNext()) {
272                 final Revision rev = (Revision) it.next();
273                 if (rev.isBeginOfLog()) {
274                     continue;
275                 }
276                 final LOCSeriesBuilder builder = (LOCSeriesBuilder) directorySeriesMap.get(rev.getFile().getDirectory());
277                 if (builder == null) {
278                     continue; // minor directory
279                 }
280                 builder.addRevision(rev);
281             }
282             final List directories = new ArrayList(directorySeriesMap.keySet());
283             Collections.sort(directories);
284             final List result = new ArrayList();
285             it = directories.iterator();
286             while (it.hasNext()) {
287                 final Directory directory = (Directory) it.next();
288                 final LOCSeriesBuilder builder = (LOCSeriesBuilder) directorySeriesMap.get(directory);
289                 final TimeSeries series = builder.getTimeSeries();
290                 if (series != null) {
291                     result.add(series);
292                 }
293             }
294             return result;
295         }
296 
297         public AllDirectoriesLOCChartMaker(final ReportConfig config, final int showMaxDirectories) {
298             super(config, createAllDirectoriesLOCSeries(config.getRepository(), showMaxDirectories), "Lines of Code Per Directory",
299                     "directories_loc_timeline.png", config.getLargeChartSize(), SymbolicNameAnnotation.createAnnotations(config.getRepository()
300                             .getSymbolicNames()));
301         }
302     }
303 }