com.sun.japex.ChartGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.japex.ChartGenerator.java

Source

/*
 * Japex software ("Software")
 *
 * Copyright, 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Software is licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. You may
 * obtain a copy of the License at:
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations.
 *
 *    Sun supports and benefits from the global community of open source
 * developers, and thanks the community for its important contributions and
 * open standards-based technology, which Sun has adopted into many of its
 * products.
 *
 *    Please note that portions of Software may be provided with notices and
 * open source licenses from such communities and third parties that govern the
 * use of those portions, and any licenses granted hereunder do not alter any
 * rights and obligations you may have under such open source licenses,
 * however, the disclaimer of warranty and limitation of liability provisions
 * in this License will apply to all Software in this distribution.
 *
 *    You acknowledge that the Software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any nuclear
 * facility.
 *
 * Apache License
 * Version 2.0, January 2004
 * http://www.apache.org/licenses/
 *
 */

package com.sun.japex;

import java.util.*;
import java.awt.Shape;
import java.awt.BasicStroke;
import java.awt.Polygon;
import java.awt.Color;
import java.awt.Font;
import java.io.File;

import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.data.xy.*;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
import org.jfree.data.general.SeriesException;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer3D;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;

public class ChartGenerator {

    /**
     * Test suite for which chart will be generated.
     */
    TestSuiteImpl _testSuite;

    /**
     * Chart width and height as a function of the number of
     * drivers in the test suite.
     */
    int _chartWidth, _chartHeight;

    /**
     * Suggested group size to use when plotting test cases. This
     * is a maximum value. The actual number of tests per plot may
     * vary but will not exceed this number.
     */
    int _plotGroupSize;

    /**
     * By default, test cases are plotted over the list of drivers.
     * By setting this flag, drivers are plotted over the list of
     * test cases instead.
     */
    boolean _plotDrivers;

    public ChartGenerator(TestSuiteImpl testSuite) {
        _testSuite = testSuite;

        _plotGroupSize = testSuite.getIntParam(Constants.PLOT_GROUP_SIZE);
        _plotDrivers = testSuite.getBooleanParam(Constants.PLOT_DRIVERS);

        // Calculate charts width and height (min = 750, max = 1500)
        List<DriverImpl> driverInfoList = _testSuite.getDriverInfoList();
        int n = !_plotDrivers ? driverInfoList.size()
                : ((DriverImpl) driverInfoList.get(0)).getAggregateTestCases().size();
        _chartWidth = Math.min(Math.max(n * 80, 750), 1500);
        _chartHeight = (int) Math.round(_chartWidth * 0.6);
    }

    public void generateDriverChart(String fileName) {
        try {
            JFreeChart chart = null;
            String chartType = _testSuite.getParam(Constants.CHART_TYPE);

            if (chartType.equalsIgnoreCase("barchart")) {
                chart = generateDriverBarChart();
            } else if (chartType.equalsIgnoreCase("scatterchart")) {
                chart = generateDriverScatterChart();
            } else if (chartType.equalsIgnoreCase("linechart")) {
                chart = generateDriverLineChart();
            } else {
                assert false;
            }

            chart.setAntiAlias(true);
            ChartUtilities.saveChartAsJPEG(new File(fileName), chart, _chartWidth, _chartHeight);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private JFreeChart generateDriverBarChart() {
        try {
            String resultUnit = _testSuite.getParam(Constants.RESULT_UNIT);
            String resultUnitX = _testSuite.getParam(Constants.RESULT_UNIT_X);

            // Ensure japex.resultUnitX is not null
            if (resultUnitX == null) {
                resultUnitX = "";
            }

            DefaultCategoryDataset dataset = new DefaultCategoryDataset();
            DefaultCategoryDataset datasetX = new DefaultCategoryDataset();

            // Find first normalizer driver (if any) and adjust unit            
            DriverImpl normalizerDriver = null;
            for (DriverImpl driver : _testSuite.getDriverInfoList()) {
                if (driver.isNormal()) {
                    normalizerDriver = driver;
                    break;
                }
            }

            // Check if normalizer driver can be used as such
            if (normalizerDriver != null) {
                if (normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN) == 0.0
                        || normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN) == 0.0
                        || normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN) == 0.0) {
                    System.out.println("Warning: Driver '" + normalizerDriver.getName()
                            + "' cannot be used to normalize results");
                    normalizerDriver = null;
                } else {
                    resultUnit = "% of " + resultUnit;
                    if (resultUnitX != null) {
                        resultUnitX = "% of " + resultUnitX;
                    }
                }
            }

            boolean hasValueX = false;

            // Generate charts
            for (DriverImpl di : _testSuite.getDriverInfoList()) {
                if (normalizerDriver != null) {
                    dataset.addValue(
                            normalizerDriver == di ? 100.0
                                    : (100.0 * di.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN)
                                            / normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN)),
                            di.getName(), "Arithmetic Mean");
                    dataset.addValue(
                            normalizerDriver == di ? 100.0
                                    : (100.0 * di.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN)
                                            / normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN)),
                            di.getName(), "Geometric Mean");
                    dataset.addValue(
                            normalizerDriver == di ? 100.0
                                    : (100.0 * di.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN)
                                            / normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN)),
                            di.getName(), "Harmonic Mean");

                    if (di.hasParam(Constants.RESULT_ARIT_MEAN_X)) {
                        datasetX.addValue(
                                normalizerDriver == di ? 100.0
                                        : (100.0 * di.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN_X)
                                                / normalizerDriver
                                                        .getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN_X)),
                                di.getName(), "Arithmetic Mean");
                        datasetX.addValue(
                                normalizerDriver == di ? 100.0
                                        : (100.0 * di.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN_X)
                                                / normalizerDriver
                                                        .getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN_X)),
                                di.getName(), "Geometric Mean");
                        datasetX.addValue(
                                (100.0 * di.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN_X)
                                        / normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN_X)),
                                di.getName(), "Harmonic Mean");
                        hasValueX = true;
                    }
                } else {
                    dataset.addValue(di.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN), di.getName(),
                            "Arithmetic Mean");
                    dataset.addValue(di.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN), di.getName(),
                            "Geometric Mean");
                    dataset.addValue(di.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN), di.getName(),
                            "Harmonic Mean");

                    if (di.hasParam(Constants.RESULT_ARIT_MEAN_X)) {
                        datasetX.addValue(di.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN_X), di.getName(),
                                "Arithmetic Mean");
                        datasetX.addValue(di.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN_X), di.getName(),
                                "Geometric Mean");
                        datasetX.addValue(di.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN_X), di.getName(),
                                "Harmonic Mean");
                        hasValueX = true;
                    }
                }

            }

            int nextPlotIndex = 1;
            CombinedDomainCategoryPlot plot = new CombinedDomainCategoryPlot();

            // Use same renderer in combine charts to get same colors
            BarRenderer3D renderer = new BarRenderer3D();
            renderer.setToolTipGenerator(new StandardCategoryToolTipGenerator());

            // Bar chart for secondary data set based on japex.resultValueX
            if (hasValueX) {
                NumberAxis rangeAxisX = new NumberAxis(resultUnitX);
                CategoryPlot subplotX = new CategoryPlot(datasetX, null, rangeAxisX, renderer);

                // Set transparency and clear legend for this plot
                subplotX.setForegroundAlpha(0.7f);
                subplotX.setFixedLegendItems(new LegendItemCollection());

                plot.add(subplotX, nextPlotIndex++);
                _chartHeight += 50; // Adjust chart height
            } else {
            }

            // Bar chart for main data set based on japex.resultValue
            NumberAxis rangeAxis = new NumberAxis(resultUnit);
            CategoryPlot subplot = new CategoryPlot(dataset, null, rangeAxis, renderer);
            subplot.setForegroundAlpha(0.7f); // transparency
            plot.add(subplot, nextPlotIndex);

            // Create chart and save it as JPEG
            String chartTitle = "Result Summary";
            JFreeChart chart = new JFreeChart(hasValueX ? chartTitle : (chartTitle + "(" + resultUnit + ")"),
                    new Font("SansSerif", Font.BOLD, 14), plot, true);
            chart.setAntiAlias(true);
            return chart;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private JFreeChart generateDriverScatterChart() {
        try {
            DefaultTableXYDataset xyDataset = new DefaultTableXYDataset();

            // Generate charts
            for (DriverImpl di : _testSuite.getDriverInfoList()) {
                if (!di.hasParam(Constants.RESULT_ARIT_MEAN_X)) {
                    System.out.println("Error: Driver '" + di.getName() + "' does not define"
                            + " any values for the X axis needed to generate a scatter chart");
                    System.exit(1);
                }

                XYSeries xySeries = new XYSeries(di.getName(), true, false);
                xySeries.add(di.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN_X),
                        di.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN));
                xySeries.add(di.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN_X),
                        di.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN));
                xySeries.add(di.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN_X),
                        di.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN));
                xyDataset.addSeries(xySeries);
            }

            String resultUnit = _testSuite.getParam(Constants.RESULT_UNIT);
            String resultUnitX = _testSuite.getParam(Constants.RESULT_UNIT_X);

            JFreeChart chart = ChartFactory.createScatterPlot("Result Summary", resultUnitX, resultUnit, xyDataset,
                    PlotOrientation.VERTICAL, true, true, false);

            // Set log scale depending on japex.resultAxis[_X]
            XYPlot plot = chart.getXYPlot();
            if (_testSuite.getParam(Constants.RESULT_AXIS_X).equalsIgnoreCase("logarithmic")) {
                LogarithmicAxis logAxisX = new LogarithmicAxis(resultUnitX);
                logAxisX.setAllowNegativesFlag(true);
                plot.setDomainAxis(logAxisX);
            }
            if (_testSuite.getParam(Constants.RESULT_AXIS).equalsIgnoreCase("logarithmic")) {
                LogarithmicAxis logAxis = new LogarithmicAxis(resultUnit);
                logAxis.setAllowNegativesFlag(true);
                plot.setRangeAxis(logAxis);
            }

            return chart;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private JFreeChart generateDriverLineChart() {
        try {
            String resultUnit = _testSuite.getParam(Constants.RESULT_UNIT);
            DefaultCategoryDataset dataset = new DefaultCategoryDataset();

            // Generate charts
            for (DriverImpl di : _testSuite.getDriverInfoList()) {
                dataset.addValue(di.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN), "Arithmetic Mean",
                        di.getName());
                dataset.addValue(di.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN), "Geometric Mean",
                        di.getName());
                dataset.addValue(di.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN), "Harmonic Mean", di.getName());
            }

            JFreeChart chart = ChartFactory.createLineChart("Result Summary (" + resultUnit + ")", "", resultUnit,
                    dataset, PlotOrientation.VERTICAL, true, true, false);

            configureLineChart(chart);
            return chart;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public int generateTestCaseCharts(String baseName, String extension) {
        String chartType = _testSuite.getParam(Constants.CHART_TYPE);
        if (chartType.equalsIgnoreCase("barchart")) {
            return generateTestCaseBarCharts(baseName, extension);
        } else if (chartType.equalsIgnoreCase("scatterchart")) {
            return generateTestCaseScatterCharts(baseName, extension);
        } else if (chartType.equalsIgnoreCase("linechart")) {
            return generateTestCaseLineCharts(baseName, extension);
        } else {
            assert false;
        }
        return 0;
    }

    private int generateTestCaseBarCharts(String baseName, String extension) {
        int nOfFiles = 0;
        List<DriverImpl> driverInfoList = _testSuite.getDriverInfoList();

        // Get number of tests from first driver
        final int nOfTests = driverInfoList.get(0).getAggregateTestCases().size();

        int groupSizesIndex = 0;
        int[] groupSizes = calculateGroupSizes(nOfTests, _plotGroupSize);

        try {
            String resultUnit = _testSuite.getParam(Constants.RESULT_UNIT);
            String resultUnitX = _testSuite.getParam(Constants.RESULT_UNIT_X);

            // Ensure japex.resultUnitX is not null
            if (resultUnitX == null) {
                resultUnitX = "";
            }

            // Find first normalizer driver (if any)
            DriverImpl normalizerDriver = null;
            for (DriverImpl di : driverInfoList) {
                if (di.isNormal()) {
                    normalizerDriver = di;
                    break;
                }
            }

            // Check if normalizer driver can be used as such
            if (normalizerDriver != null) {
                if (normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_ARIT_MEAN) == 0.0
                        || normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_GEOM_MEAN) == 0.0
                        || normalizerDriver.getDoubleParamNoNaN(Constants.RESULT_HARM_MEAN) == 0.0) {
                    normalizerDriver = null;
                } else {
                    resultUnit = "% of " + resultUnit;
                    if (resultUnitX != null) {
                        resultUnitX = "% of " + resultUnitX;
                    }
                }
            }

            // Generate charts 
            DefaultCategoryDataset dataset = new DefaultCategoryDataset();
            DefaultCategoryDataset datasetX = new DefaultCategoryDataset();

            boolean hasValueX = false;

            int i = 0, thisGroupSize = 0;
            for (; i < nOfTests; i++) {

                for (DriverImpl di : driverInfoList) {
                    TestCaseImpl tc = (TestCaseImpl) di.getAggregateTestCases().get(i);

                    // User normalizer driver if defined
                    if (normalizerDriver != null) {
                        TestCaseImpl normalTc = (TestCaseImpl) normalizerDriver.getAggregateTestCases().get(i);

                        dataset.addValue(
                                normalizerDriver == di ? 100.0
                                        : (100.0 * tc.getDoubleParamNoNaN(Constants.RESULT_VALUE)
                                                / normalTc.getDoubleParamNoNaN(Constants.RESULT_VALUE)),
                                _plotDrivers ? tc.getName() : di.getName(),
                                _plotDrivers ? di.getName() : tc.getName());

                        if (tc.hasParam(Constants.RESULT_VALUE_X)) {
                            datasetX.addValue(
                                    (100.0 * tc.getDoubleParamNoNaN(Constants.RESULT_VALUE_X)
                                            / normalTc.getDoubleParamNoNaN(Constants.RESULT_VALUE_X)),
                                    _plotDrivers ? tc.getName() : di.getName(),
                                    _plotDrivers ? di.getName() : tc.getName());
                            hasValueX = true;
                        }
                    } else {
                        dataset.addValue(tc.getDoubleParamNoNaN(Constants.RESULT_VALUE),
                                _plotDrivers ? tc.getName() : di.getName(),
                                _plotDrivers ? di.getName() : tc.getName());

                        if (tc.hasParam(Constants.RESULT_VALUE_X)) {
                            datasetX.addValue(tc.getDoubleParamNoNaN(Constants.RESULT_VALUE_X),
                                    _plotDrivers ? tc.getName() : di.getName(),
                                    _plotDrivers ? di.getName() : tc.getName());
                            hasValueX = true;
                        }
                    }

                }

                thisGroupSize++;

                // Generate chart for this group if complete
                if (thisGroupSize == groupSizes[groupSizesIndex]) {
                    int nextPlotIndex = 1;
                    CombinedDomainCategoryPlot plot = new CombinedDomainCategoryPlot();

                    // Use same renderer in combine charts to get same colors
                    BarRenderer3D renderer = new BarRenderer3D();
                    renderer.setToolTipGenerator(new StandardCategoryToolTipGenerator());

                    // Bar chart for secondary data set based on japex.resultValueX
                    if (hasValueX) {
                        NumberAxis rangeAxisX = new NumberAxis(resultUnitX);
                        CategoryPlot subplotX = new CategoryPlot(datasetX, null, rangeAxisX, renderer);

                        // Set transparency and clear legend for this plot
                        subplotX.setForegroundAlpha(0.7f);
                        subplotX.setFixedLegendItems(new LegendItemCollection());

                        plot.add(subplotX, nextPlotIndex++);
                        _chartHeight += 50; // Adjust chart height
                    }

                    // Bar chart for main data set based on japex.resultValue
                    NumberAxis rangeAxis = new NumberAxis(resultUnit);
                    CategoryPlot subplot = new CategoryPlot(dataset, null, rangeAxis, renderer);
                    subplot.setForegroundAlpha(0.7f); // transparency
                    plot.add(subplot, nextPlotIndex);

                    // Create chart and save it as JPEG
                    String chartTitle = _plotDrivers ? "Results per Driver" : "Results per Test";
                    JFreeChart chart = new JFreeChart(
                            hasValueX ? chartTitle : (chartTitle + "(" + resultUnit + ")"),
                            new Font("SansSerif", Font.BOLD, 14), plot, true);
                    chart.setAntiAlias(true);
                    ChartUtilities.saveChartAsJPEG(new File(baseName + Integer.toString(nOfFiles) + extension),
                            chart, _chartWidth, _chartHeight);

                    nOfFiles++;
                    groupSizesIndex++;
                    thisGroupSize = 0;

                    // Create fresh data sets
                    dataset = new DefaultCategoryDataset();
                    datasetX = new DefaultCategoryDataset();
                }
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return nOfFiles;
    }

    private int generateTestCaseLineCharts(String baseName, String extension) {
        int nOfFiles = 0;
        List<DriverImpl> driverInfoList = _testSuite.getDriverInfoList();

        // Get number of tests from first driver
        final int nOfTests = driverInfoList.get(0).getAggregateTestCases().size();

        int groupSizesIndex = 0;
        int[] groupSizes = calculateGroupSizes(nOfTests, _plotGroupSize);

        try {
            String resultUnit = _testSuite.getParam(Constants.RESULT_UNIT);

            // Generate charts 
            DefaultCategoryDataset dataset = new DefaultCategoryDataset();

            int i = 0, thisGroupSize = 0;
            for (; i < nOfTests; i++) {

                for (DriverImpl di : driverInfoList) {
                    TestCaseImpl tc = (TestCaseImpl) di.getAggregateTestCases().get(i);

                    dataset.addValue(tc.getDoubleParamNoNaN(Constants.RESULT_VALUE),
                            _plotDrivers ? di.getName() : tc.getName(), _plotDrivers ? tc.getName() : di.getName());
                }

                thisGroupSize++;

                // Generate chart for this group if complete
                if (thisGroupSize == groupSizes[groupSizesIndex]) {
                    JFreeChart chart = ChartFactory.createLineChart(
                            (_plotDrivers ? "Results per Driver (" : "Results per Test (") + resultUnit + ")", "",
                            resultUnit, dataset, PlotOrientation.VERTICAL, true, true, false);

                    configureLineChart(chart);

                    chart.setAntiAlias(true);
                    ChartUtilities.saveChartAsJPEG(new File(baseName + Integer.toString(nOfFiles) + extension),
                            chart, _chartWidth, _chartHeight);

                    nOfFiles++;
                    groupSizesIndex++;
                    thisGroupSize = 0;
                    dataset = new DefaultCategoryDataset();
                }
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return nOfFiles;
    }

    private int generateTestCaseScatterCharts(String baseName, String extension) {
        int nOfFiles = 0;
        List<DriverImpl> driverInfoList = _testSuite.getDriverInfoList();

        try {
            // Get number of tests from first driver
            final int nOfTests = driverInfoList.get(0).getAggregateTestCases().size();

            DefaultTableXYDataset xyDataset = new DefaultTableXYDataset();

            // Generate charts
            for (DriverImpl di : driverInfoList) {
                XYSeries xySeries = new XYSeries(di.getName(), true, false);
                for (int j = 0; j < nOfTests; j++) {
                    TestCaseImpl tc = (TestCaseImpl) di.getAggregateTestCases().get(j);
                    try {
                        xySeries.add(tc.getDoubleParamNoNaN(Constants.RESULT_VALUE_X),
                                tc.getDoubleParamNoNaN(Constants.RESULT_VALUE));
                    } catch (SeriesException e) {
                        // Ignore duplicate x-valued points
                    }

                }
                xyDataset.addSeries(xySeries);
            }

            String resultUnit = _testSuite.getParam(Constants.RESULT_UNIT);
            String resultUnitX = _testSuite.getParam(Constants.RESULT_UNIT_X);

            JFreeChart chart = ChartFactory.createScatterPlot("Results Per Test", resultUnitX, resultUnit,
                    xyDataset, PlotOrientation.VERTICAL, true, true, false);

            // Set log scale depending on japex.resultAxis[_X]
            XYPlot plot = chart.getXYPlot();
            if (_testSuite.getParam(Constants.RESULT_AXIS_X).equalsIgnoreCase("logarithmic")) {
                LogarithmicAxis logAxisX = new LogarithmicAxis(resultUnitX);
                logAxisX.setAllowNegativesFlag(true);
                plot.setDomainAxis(logAxisX);
            }
            if (_testSuite.getParam(Constants.RESULT_AXIS).equalsIgnoreCase("logarithmic")) {
                LogarithmicAxis logAxis = new LogarithmicAxis(resultUnit);
                logAxis.setAllowNegativesFlag(true);
                plot.setRangeAxis(logAxis);
            }

            chart.setAntiAlias(true);
            ChartUtilities.saveChartAsJPEG(new File(baseName + Integer.toString(nOfFiles) + extension), chart,
                    _chartWidth, _chartHeight);
            nOfFiles++;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return nOfFiles;
    }

    static private void configureLineChart(JFreeChart chart) {
        CategoryPlot plot = chart.getCategoryPlot();

        final DrawingSupplier supplier = new DefaultDrawingSupplier(DefaultDrawingSupplier.DEFAULT_PAINT_SEQUENCE,
                DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE,
                DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE,
                DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE,
                // Draw a small diamond 
                new Shape[] { new Polygon(new int[] { 3, 0, -3, 0 }, new int[] { 0, 3, 0, -3 }, 4) });
        plot.setDomainGridlinePaint(Color.black);
        plot.setRangeGridlinePaint(Color.black);
        plot.setDrawingSupplier(supplier);

        LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
        renderer.setShapesVisible(true);
        renderer.setStroke(new BasicStroke(2.0f));
    }

    /**
     * Calculate group sizes for tests to avoid a very small final group. 
     * For example, calculateGroupSizes(21, 5) return { 5,5,5,3,3 } instead
     * of { 5,5,5,5,1 }.
     */
    private static int[] calculateGroupSizes(int nOfTests, int maxGroupSize) {
        if (nOfTests <= maxGroupSize) {
            return new int[] { nOfTests };
        }

        int[] result = new int[nOfTests / maxGroupSize + ((nOfTests % maxGroupSize > 0) ? 1 : 0)];

        // Var m1 represents the number of groups of size maxGroupSize
        int m1 = (nOfTests - maxGroupSize) / maxGroupSize;
        for (int i = 0; i < m1; i++) {
            result[i] = maxGroupSize;
        }

        // Var m2 represents the number of tests not allocated into groups
        int m2 = nOfTests - m1 * maxGroupSize;
        if (m2 <= maxGroupSize) {
            result[result.length - 1] = m2;
        } else {
            // Allocate last two groups
            result[result.length - 2] = (int) Math.ceil(m2 / 2.0);
            result[result.length - 1] = m2 - result[result.length - 2];
        }
        return result;
    }

}