View Javadoc

1   /**
2    * 
3    */
4   package pt.digitalis.sampleApp.util.stats;
5   
6   import java.awt.BasicStroke;
7   import java.awt.Color;
8   import java.awt.image.BufferedImage;
9   import java.io.FileNotFoundException;
10  import java.io.FileOutputStream;
11  import java.io.IOException;
12  import java.util.ArrayList;
13  import java.util.List;
14  
15  import org.apache.poi.hssf.usermodel.HSSFRichTextString;
16  import org.apache.poi.hssf.usermodel.HSSFRow;
17  import org.apache.poi.hssf.usermodel.HSSFSheet;
18  import org.apache.poi.hssf.usermodel.HSSFWorkbook;
19  import org.jfree.chart.ChartFactory;
20  import org.jfree.chart.ChartUtilities;
21  import org.jfree.chart.JFreeChart;
22  import org.jfree.chart.axis.CategoryAxis;
23  import org.jfree.chart.axis.CategoryLabelPositions;
24  import org.jfree.chart.axis.NumberAxis;
25  import org.jfree.chart.plot.CategoryPlot;
26  import org.jfree.chart.plot.PlotOrientation;
27  import org.jfree.chart.renderer.category.LineAndShapeRenderer;
28  import org.jfree.data.category.DefaultCategoryDataset;
29  
30  import pt.digitalis.log.ILogWrapper;
31  import pt.digitalis.sampleApp.util.Utilities;
32  
33  /**
34   * @author Pedro Viegas <a
35   *         href="mailto:pviegas@digitalis.pt">pviegas@digitalis.pt</a>
36   * @created Aug 8, 2007
37   * 
38   */
39  public class StatsGenerator {
40  
41  	/** Default XAxis count */
42  	final private int DEFAULT_XCOUNT = 20;
43  
44  	/**
45  	 * A list of series to calculate statistcs on...
46  	 */
47  	private List<Series> series = new ArrayList<Series>();
48  
49  	/** Max time scale */
50  	private long maxTime;
51  
52  	/** The generated X Axis Values */
53  	private long[] XAxisValues;
54  
55  	/** The generated Y Axis Values for each serie */
56  	private long[][] YAxisValues;
57  
58  	/** The logger */
59  	private ILogWrapper logger;
60  
61  	/**
62  	 * Constructor
63  	 * 
64  	 * @param logger
65  	 *            the logger to use
66  	 */
67  	public StatsGenerator(ILogWrapper logger) {
68  		this.logger = logger;
69  	}
70  
71  	/**
72  	 * Add a serie to the list
73  	 * 
74  	 * @param serie
75  	 */
76  	public void addSerie(Series serie) {
77  		series.add(serie);
78  	}
79  
80  	/**
81  	 * Add a group of values to the list
82  	 * 
83  	 * @param name
84  	 * @param values
85  	 */
86  	public void addSerie(String name, long[] values) {
87  		addSerie(new Series(name, values));
88  	}
89  
90  	/**
91  	 * Reads the series and created new chart data. Overrides previous ones if
92  	 * they existed
93  	 * 
94  	 * @param xCount
95  	 *            the total of X values desired (precision)
96  	 */
97  	public void buildChartData(int xCount) {
98  
99  		XAxisValues = new long[xCount];
100 		YAxisValues = new long[series.size()][xCount];
101 
102 		maxTime = 0;
103 
104 		// Parse the time values to see witch is the X Axis max value
105 		for (Series s : series) {
106 			long[] values = s.getValues();
107 
108 			for (int i = 0; i < values.length; i++) {
109 				if (values[i] > maxTime)
110 					maxTime = values[i];
111 			}
112 		}
113 
114 		// Fill the X AXis values
115 		long xStep = maxTime / xCount;
116 		long xValue = 0;
117 
118 		for (int i = 0; i < xCount; i++) {
119 			XAxisValues[i] = xValue;
120 			xValue += xStep;
121 
122 			// Initialize all series with 0;
123 			for (int j = 0; j < series.size(); j++)
124 				YAxisValues[j][i] = 0;
125 		}
126 
127 		// Fill the Y Axis Values
128 		int serieIndex = 0;
129 
130 		for (Series s : series) {
131 			long[] values = s.getValues();
132 
133 			for (int i = 0; i < values.length; i++) {
134 
135 				boolean found = false;
136 
137 				// -1 execution time means an error!
138 				if (values[i] != -1) {
139 
140 					// For each value, let's see what X value it corresponds to
141 					for (int j = 0; j < XAxisValues.length; j++) {
142 
143 						if (!found)
144 							if ((j == XAxisValues.length - 1)
145 									|| (values[i] < XAxisValues[j])) {
146 								// If it's the last X value or it's larger than
147 								// the current value it is the previous one. All
148 								// values since the previous will have this
149 								// added to them
150 
151 								found = true;
152 
153 								if (j > 0)
154 									YAxisValues[serieIndex][j - 1] = YAxisValues[serieIndex][j - 1] + 1;
155 							}
156 
157 						if (found)
158 							YAxisValues[serieIndex][j] = YAxisValues[serieIndex][j] + 1;
159 					}
160 				}
161 			}
162 
163 			serieIndex++;
164 		}
165 	}
166 
167 	/**
168 	 * Exports the stats to Excel
169 	 * 
170 	 * @param fileName
171 	 *            the name of the EXCEL file to export to
172 	 */
173 	public void exportToExcel(String fileName) {
174 		HSSFWorkbook wb = new HSSFWorkbook();
175 		HSSFSheet sheet = wb.createSheet("ISS Performance Execution data");
176 
177 		logger.debug("Creating Workbook...");
178 
179 		if (XAxisValues == null || YAxisValues == null)
180 			buildChartData(DEFAULT_XCOUNT);
181 
182 		int xCount = XAxisValues.length;
183 
184 		int seriesIndex = 0;
185 
186 		for (Series s : series) {
187 
188 			// Created a new row of data
189 			// Start at 3rd row (1st is 0) for readability
190 			HSSFRow row = sheet.createRow((short) seriesIndex + 2);
191 
192 			// Create the series label cell
193 			row.createCell((short) 1).setCellValue(
194 					new HSSFRichTextString(s.getName()));
195 
196 			for (int i = 0; i < xCount; i++) {
197 
198 				// Create a cell and put a value in it.
199 				row.createCell((short) (i + 2)).setCellValue(
200 						YAxisValues[seriesIndex][i]);
201 			}
202 
203 			seriesIndex++;
204 		}
205 
206 		// Created the XAxis data row
207 		// Start at 3rd row (1st is 0) for readability
208 		HSSFRow row = sheet.createRow((short) seriesIndex + 2);
209 
210 		for (int i = 0; i < xCount; i++)
211 			row.createCell((short) (i + 2)).setCellValue(XAxisValues[i]);
212 
213 		logger.debug("Workbook created...");
214 
215 		// Write the output to a file
216 		FileOutputStream fileOut;
217 		try {
218 			fileOut = new FileOutputStream(fileName);
219 			wb.write(fileOut);
220 			fileOut.close();
221 			logger.info("Created an export of the Performance data in \""
222 					+ fileName + "\"...");
223 
224 		} catch (FileNotFoundException e) {
225 			logger.error("Excel export aborted: The file \"" + fileName
226 					+ "\" could not be created.");
227 			e.printStackTrace();
228 
229 		} catch (IOException e) {
230 			logger
231 					.error("Excel export aborted: There was an error writing to \""
232 							+ fileName + "\".");
233 			e.printStackTrace();
234 
235 		}
236 	}
237 
238 	/**
239 	 * Creates a Line Chart and saves it to disk
240 	 * 
241 	 * @param fileName
242 	 *            the name of the image file to export to
243 	 */
244 	public void createChartAsPNG(String fileName) {
245 
246 		String scale = "(ms)";
247 		int scaleFactor = 1;
248 
249 		if (maxTime > 999)
250 			if ((maxTime / 1000) > 999) {
251 
252 				// Minutes scale
253 				scale = "(m)";
254 				scaleFactor = 1000 * 60;
255 
256 			} else {
257 
258 				// Seconds scale
259 				scale = "(s)";
260 				scaleFactor = 1000;
261 			}
262 
263 		logger.debug("Creating Chart...");
264 
265 		if (XAxisValues == null || YAxisValues == null)
266 			buildChartData(DEFAULT_XCOUNT);
267 
268 		int xCount = XAxisValues.length;
269 		int seriesIndex = 0;
270 
271 		// A new dataset
272 		DefaultCategoryDataset dataset = new DefaultCategoryDataset();
273 
274 		// Fill the dataset with all series values
275 		for (Series s : series) {
276 			for (int i = 0; i < xCount; i++) {
277 
278 				// Add the value to the dataset to the associated series
279 				// and X Value
280 				dataset.addValue(YAxisValues[seriesIndex][i], s.getName(), Long
281 						.toString(XAxisValues[i] / scaleFactor));
282 			}
283 
284 			seriesIndex++;
285 		}
286 
287 		JFreeChart chart = ChartFactory.createLineChart(
288 				"ISS Performance Chart", // chart title
289 				"Time " + scale, // domain axis label
290 				"Processes", // range axis label
291 				dataset, // data
292 				PlotOrientation.VERTICAL, // orientation
293 				true, // include legend
294 				true, // tooltips
295 				false // urls
296 				);
297 
298 		logger.debug("Chart initialized...");
299 		logger.debug("Customizing chart...");
300 
301 		chart.setBackgroundPaint(Color.white);
302 
303 		CategoryPlot plot = (CategoryPlot) chart.getPlot();
304 		plot.setBackgroundPaint(Color.decode("0xffffe0")); // Light yellow
305 
306 		// customize the range axis...
307 		NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
308 		rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
309 		rangeAxis.setAutoRangeIncludesZero(true);
310 
311 		// customize the domain axis...
312 		if ((maxTime / scaleFactor) > 9) {
313 
314 			// More than 1 digit will be probably truncated, so we will show
315 			// them rotated by 90ยบ
316 			CategoryAxis xAxis = plot.getDomainAxis();
317 			xAxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
318 		}
319 
320 		// Customize the renderer...
321 		LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot
322 				.getRenderer();
323 		// LineRenderer3D renderer = (LineRenderer3D) plot.getRenderer();
324 		// renderer.setXOffset(8);
325 
326 		renderer.setSeriesStroke(0, new BasicStroke(2.0f,
327 				BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f,
328 				new float[] { 10.0f, 6.0f }, 0.0f));
329 		renderer.setSeriesStroke(1, new BasicStroke(2.0f,
330 				BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f,
331 				new float[] { 2.0f, 6.0f }, 0.0f));
332 		renderer.setSeriesStroke(2, new BasicStroke(2.0f,
333 				BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
334 
335 		logger.debug("Chart configured...");
336 
337 		logger.debug("Creating chart image...");
338 		BufferedImage bi = chart.createBufferedImage(550, 400);
339 
340 		// Write the output to a file
341 		FileOutputStream fileOut;
342 		try {
343 			fileOut = new FileOutputStream(fileName);
344 
345 			ChartUtilities.writeBufferedImageAsPNG(fileOut, bi);
346 
347 			fileOut.close();
348 			logger.info("Created a chart of the Performance data in \""
349 					+ fileName + "\"...");
350 
351 		} catch (FileNotFoundException e) {
352 			logger.error("Image export aborted: The file \"" + fileName
353 					+ "\" could not be created.");
354 			e.printStackTrace();
355 
356 		} catch (IOException e) {
357 			logger
358 					.error("Image Chart export aborted: There was an error writing to \""
359 							+ fileName + "\".");
360 			e.printStackTrace();
361 
362 		}
363 	}
364 
365 	/**
366 	 * Prints an ascii art chart to the standard output
367 	 */
368 	public void printAsciiChart() {
369 
370 		String scale = "(ms)";
371 		int scaleFactor = 1;
372 
373 		if (XAxisValues == null || YAxisValues == null)
374 			buildChartData(DEFAULT_XCOUNT);
375 
376 		if (maxTime > 999)
377 			if ((maxTime / 60) > 999) {
378 
379 				// Minutes scale
380 				scale = "(m)";
381 				scaleFactor = 1024 * 60;
382 
383 			} else {
384 
385 				// Seconds scale
386 				scale = "(s)";
387 				scaleFactor = 1024;
388 			}
389 
390 		int xCount = XAxisValues.length;
391 		int seriesIndex = 0;
392 
393 		if (logger.isDebugEnabled()) {
394 			logger.debug("MaxTime: " + maxTime);
395 			logger.debug("Scale: " + scale);
396 			logger.debug("Scale factor: " + scaleFactor);
397 			logger.debug("XAxisDump: ");
398 
399 			for (int i = 0; i < xCount; i++) {
400 				logger.debug("   | Value #"
401 						+ Utilities.fillLeft(Long.toString(i), 3, "0")
402 						+ ": "
403 						+ Utilities.fillLeft(Long.toString(XAxisValues[i]), 10)
404 						+ "ms ["
405 						+ Utilities.fillLeft(Long.toString(XAxisValues[i]
406 								/ scaleFactor), 5) + scale + "]");
407 			}
408 		}
409 
410 		System.out.println("");
411 		System.out.println("");
412 
413 		for (Series s : series) {
414 			System.out.print(" " + Utilities.fillRight(s.getName(), 40, "...")
415 					+ " | ");
416 
417 			for (int i = 0; i < xCount; i++) {
418 				System.out.print(Utilities.fillLeft(Long
419 						.toString(YAxisValues[seriesIndex][i]), 5));
420 			}
421 
422 			System.out.println("");
423 			seriesIndex++;
424 		}
425 
426 		// Axis
427 		System.out.println(Utilities.fillRight("", 41)
428 				+ Utilities.fillRight(" -", 9 + xCount * 5, "---"));
429 
430 		// Now the X Axix
431 		System.out.print(Utilities.fillRight("", 44));
432 
433 		for (int i = 0; i < xCount; i++) {
434 			System.out.print(Utilities.fillLeft(Long.toString(XAxisValues[i]
435 					/ scaleFactor), 5));
436 		}
437 
438 		System.out.println(" " + scale);
439 		System.out.println("");
440 	}
441 }