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