/******************************************************************************* | |
* Copyright (c) 2011 Mia-Software. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* Nicolas Bros (Mia-Software) - Bug 343155 - Java discoverer time benchmark | |
*******************************************************************************/ | |
package org.eclipse.modisco.java.discoverer.benchmark.template.html; | |
import java.io.File; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.Comparator; | |
import java.util.List; | |
import org.eclipse.birt.chart.computation.Point; | |
import org.eclipse.birt.chart.device.IDeviceRenderer; | |
import org.eclipse.birt.chart.exception.ChartException; | |
import org.eclipse.birt.chart.factory.GeneratedChartState; | |
import org.eclipse.birt.chart.factory.Generator; | |
import org.eclipse.birt.chart.model.ChartWithAxes; | |
import org.eclipse.birt.chart.model.attribute.Anchor; | |
import org.eclipse.birt.chart.model.attribute.AxisType; | |
import org.eclipse.birt.chart.model.attribute.Bounds; | |
import org.eclipse.birt.chart.model.attribute.ChartDimension; | |
import org.eclipse.birt.chart.model.attribute.LineAttributes; | |
import org.eclipse.birt.chart.model.attribute.LineStyle; | |
import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl; | |
import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl; | |
import org.eclipse.birt.chart.model.attribute.impl.LineAttributesImpl; | |
import org.eclipse.birt.chart.model.attribute.impl.NumberFormatSpecifierImpl; | |
import org.eclipse.birt.chart.model.attribute.impl.TextImpl; | |
import org.eclipse.birt.chart.model.component.Axis; | |
import org.eclipse.birt.chart.model.component.Label; | |
import org.eclipse.birt.chart.model.component.Series; | |
import org.eclipse.birt.chart.model.component.impl.LabelImpl; | |
import org.eclipse.birt.chart.model.component.impl.SeriesImpl; | |
import org.eclipse.birt.chart.model.data.NumberDataSet; | |
import org.eclipse.birt.chart.model.data.SeriesDefinition; | |
import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl; | |
import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl; | |
import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl; | |
import org.eclipse.birt.chart.model.layout.LabelBlock; | |
import org.eclipse.birt.chart.model.layout.impl.LabelBlockImpl; | |
import org.eclipse.birt.chart.model.type.LineSeries; | |
import org.eclipse.birt.chart.model.type.ScatterSeries; | |
import org.eclipse.birt.chart.model.type.impl.LineSeriesImpl; | |
import org.eclipse.birt.chart.model.type.impl.ScatterSeriesImpl; | |
import org.eclipse.birt.chart.util.PluginSettings; | |
import org.eclipse.emf.common.util.EList; | |
import org.eclipse.modisco.infra.discovery.benchmark.AveragedMultiDiscoveryBenchmark; | |
import org.eclipse.modisco.infra.discovery.benchmark.AveragedProjectDiscovery; | |
import org.eclipse.modisco.infra.discovery.benchmark.Project; | |
public final class BirtGraphHelper { | |
private static final double CHART_UNIT_SPACING = 0.1; | |
public static final double MEBIBYTE = 1024.0 * 1024.0; | |
private BirtGraphHelper() { | |
// | |
} | |
public static void createBirtGraph(final AveragedMultiDiscoveryBenchmark benchmark, | |
final File targetFolder) throws ChartException { | |
ChartWithAxes chart = ChartWithAxesImpl.create(); | |
chart.setDimension(ChartDimension.TWO_DIMENSIONAL_LITERAL); | |
chart.setUnitSpacing(BirtGraphHelper.CHART_UNIT_SPACING); | |
chart.getPlot().setBackground(ColorDefinitionImpl.WHITE()); | |
chart.getPlot().getClientArea().setBackground(ColorDefinitionImpl.WHITE()); | |
// chart.getLegend().setItemType(LegendItemType.SERIES_LITERAL); | |
chart.getLegend().setVisible(false); | |
// TODO: externalize | |
chart.getTitle().getLabel().getCaption() | |
.setValue("Discovery time as function of project size"); //$NON-NLS-1$ | |
Axis xAxis = chart.getPrimaryBaseAxes()[0]; | |
xAxis.setType(AxisType.LINEAR_LITERAL); | |
xAxis.setFormatSpecifier(NumberFormatSpecifierImpl.create()); | |
// TODO: externalize | |
xAxis.getTitle().getCaption().setValue("Project size (MiB)"); //$NON-NLS-1$ | |
xAxis.getTitle().setVisible(true); | |
List<Point> dataPoints = new ArrayList<Point>(); | |
for (AveragedProjectDiscovery discovery : benchmark.getDiscoveries()) { | |
EList<Project> projects = discovery.getOccurrences().get(0).getProjects(); | |
long totalSizeInBytes = 0; | |
for (Project project : projects) { | |
totalSizeInBytes += project.getTotalSizeInBytes(); | |
} | |
dataPoints.add(new Point(totalSizeInBytes / BirtGraphHelper.MEBIBYTE, discovery | |
.getAverageExecutionTimeInSeconds())); | |
} | |
// sort on X axis | |
Collections.sort(dataPoints, new Comparator<Point>() { | |
public int compare(final Point o1, final Point o2) { | |
if (o1.x == o2.x) { | |
return 0; | |
} else if (o1.x < o2.x) { | |
return -1; | |
} else { | |
return 1; | |
} | |
} | |
}); | |
ArrayList<Double> xAxisValues = new ArrayList<Double>(); | |
for (Point dataPoint : dataPoints) { | |
xAxisValues.add(dataPoint.x); | |
} | |
NumberDataSet xAxisDataSet = NumberDataSetImpl.create(xAxisValues); | |
Series xAxisSeries = SeriesImpl.create(); | |
xAxisSeries.setDataSet(xAxisDataSet); | |
SeriesDefinition xSeriesDefinition = SeriesDefinitionImpl.create(); | |
xSeriesDefinition.getSeries().add(xAxisSeries); | |
xAxis.getSeriesDefinitions().add(xSeriesDefinition); | |
Axis yAxis = chart.getPrimaryOrthogonalAxis(xAxis); | |
yAxis.setType(AxisType.LINEAR_LITERAL); | |
yAxis.setFormatSpecifier(NumberFormatSpecifierImpl.create()); | |
yAxis.getTitle().setVisible(true); | |
// TODO: externalize | |
yAxis.getTitle().getCaption().setValue("Discovery time (seconds)"); //$NON-NLS-1$ | |
ScatterSeries lineSeries = (ScatterSeries) ScatterSeriesImpl.create(); | |
ArrayList<Double> yAxisValues = new ArrayList<Double>(); | |
for (Point dataPoint : dataPoints) { | |
yAxisValues.add(dataPoint.y); | |
} | |
NumberDataSet dataSet = NumberDataSetImpl.create(yAxisValues); | |
lineSeries.setDataSet(dataSet); | |
LineAttributes lineAttr = LineAttributesImpl.create(ColorDefinitionImpl.BLACK(), | |
LineStyle.SOLID_LITERAL, 1); | |
lineAttr.setVisible(false); | |
lineSeries.setLineAttributes(lineAttr); | |
Point[] linearRegression = computeLinearRegression(dataPoints); | |
LineSeries linearRegressionSeries = (LineSeries) LineSeriesImpl.create(); | |
ArrayList<Double> regressionYAxisValues = new ArrayList<Double>(); | |
for (Point point : linearRegression) { | |
regressionYAxisValues.add(point.y); | |
} | |
NumberDataSet linearRegressionDataSet = NumberDataSetImpl.create(regressionYAxisValues); | |
linearRegressionSeries.setDataSet(linearRegressionDataSet); | |
linearRegressionSeries.getMarkers().clear(); | |
// line attributes | |
LineAttributes lineAttr2 = LineAttributesImpl.create(ColorDefinitionImpl.BLACK(), | |
LineStyle.DASHED_LITERAL, 1); | |
lineAttr2.setVisible(true); | |
linearRegressionSeries.setLineAttributes(lineAttr2); | |
// display s/MiB | |
Point lastPoint = linearRegression[linearRegression.length - 1]; | |
LabelBlock labelBlock = (LabelBlock) LabelBlockImpl.create(); | |
Label label = LabelImpl.create(); | |
label.setCaption(TextImpl.create(String.format("%.3f s/MiB", lastPoint.y / lastPoint.x))); //$NON-NLS-1$ | |
labelBlock.setLabel(label); | |
labelBlock.setAnchor(Anchor.NORTH_EAST_LITERAL); | |
chart.getPlot().add(labelBlock); | |
SeriesDefinition ySeriesDefinition = SeriesDefinitionImpl.create(); | |
ySeriesDefinition.getSeries().add(lineSeries); | |
ySeriesDefinition.getSeries().add(linearRegressionSeries); | |
yAxis.getSeriesDefinitions().add(ySeriesDefinition); | |
PluginSettings ps = PluginSettings.instance(); | |
IDeviceRenderer render = ps.getDevice("dv.PNG"); //$NON-NLS-1$ | |
render.setProperty(IDeviceRenderer.FILE_IDENTIFIER, new File(targetFolder, | |
"executionTimeByProjectSize.png")); //$NON-NLS-1$ | |
final int width = 800; | |
final int height = 400; | |
Bounds bounds = BoundsImpl.create(0, 0, width, height); | |
Generator generator = Generator.instance(); | |
GeneratedChartState state = generator.build(render.getDisplayServer(), chart, bounds, null, | |
null, null); | |
generator.render(render, state); | |
} | |
/** | |
* Computes the linear regression of the given list of points, and returns | |
* points on the computed line. | |
* | |
* @param dataPoints | |
* the points to include in the computation | |
* @return points on the computed approximation line, with the same abscissa | |
* as the input points | |
*/ | |
private static Point[] computeLinearRegression(final List<Point> dataPoints) { | |
// see: http://en.wikipedia.org/wiki/Simple_linear_regression | |
double size = dataPoints.size(); | |
if ((int) size == 0) { | |
return new Point[0]; | |
} | |
if ((int) size == 1) { | |
Point point = dataPoints.get(0); | |
return new Point[] { new Point(point.x, point.y) }; | |
} | |
double sumXY = 0.0; | |
double sumX = 0.0; | |
double sumY = 0.0; | |
double sumXX = 0.0; | |
for (Point point : dataPoints) { | |
sumX += point.x; | |
sumXX += point.x * point.x; | |
sumY += point.y; | |
sumXY += point.x * point.y; | |
} | |
// a = (NΣXY - (ΣX)(ΣY)) / (NΣX² - (ΣX)²) | |
double a = (size * sumXY - sumX * sumY) / (size * sumXX - sumX * sumX); | |
// b = (ΣY - b(ΣX)) / N | |
double b = (sumY - a * sumX) / size; | |
List<Point> resultPoints = new ArrayList<Point>(); | |
for (Point point : dataPoints) { | |
// y = ax + b | |
resultPoints.add(new Point(point.x, a * point.x + b)); | |
} | |
return resultPoints.toArray(new Point[resultPoints.size()]); | |
} | |
} |