/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package br.uff.ic.oceano.ostra.service; import br.uff.ic.oceano.core.factory.ObjectFactory; import br.uff.ic.oceano.core.model.Metric; import br.uff.ic.oceano.core.model.MetricValue; import br.uff.ic.oceano.core.model.SoftwareProject; import br.uff.ic.oceano.core.service.MetricValueService; import br.uff.ic.oceano.util.DateUtil; import br.uff.ic.oceano.ostra.decorator.RevisionMetricValueDto; import java.awt.Color; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.servlet.ServletContext; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.StandardEntityCollection; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.data.xy.XIntervalSeries; import org.jfree.data.xy.XIntervalSeriesCollection; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; /** * * @author Wallace */ public class MetricChartService { private static final String DEFAULT_CHART_PATH = "/privado/ostra/chart/temp/"; private MetricValueService metricValueService = ObjectFactory.getObjectWithDataBaseDependencies(MetricValueService.class); private OstraMetricValueService ostraMetricValueService = ObjectFactory.getObjectWithDataBaseDependencies(OstraMetricValueService.class); //---- control private final boolean USE_RELATIVE_REVISION_NUMBER = true; private final boolean USE_RELATIVE_COMMIT_DATE = !USE_RELATIVE_REVISION_NUMBER; //---- //Buffers for charts private SoftwareProject softwareProjectBuffer; //marks current data origen private Metric metricBuffer; //marks current data origen private List<RevisionMetricValueDto> dataValuesBuffer; private List<RevisionMetricValueDto> deltaValuesBuffer; private List<RevisionMetricValueDto> averageValuesBuffer; public ChartValue getChartValue(SoftwareProject softwareProject, Metric metric, int x, int y, boolean isDelta, ServletContext sc) { String metricName = metric.getName(); System.out.println("criando grafico de " + metricName); final ChartValue chartValue = new ChartValue(); List<RevisionMetricValueDto> dataValues = getDataValues(softwareProject, metric); List<RevisionMetricValueDto> deltaValues = getDeltaValues(softwareProject, metric); final String chartPath = DEFAULT_CHART_PATH + "DynamicChart" + System.currentTimeMillis() + ".png"; chartValue.setChartPath(chartPath); XYSeriesCollection dataSet = createDataSet(dataValues, false); final ChartRenderingInfo info = this.createChart(x, y, dataSet, metricName, chartPath, sc); final String tag = this.createImageMap(info, dataValues, false, "imgMap"); chartValue.setTag(tag); final String deltaChartPath = DEFAULT_CHART_PATH + "DynamicChart" + System.currentTimeMillis() + ".png"; XYSeriesCollection deltaSet = createDataSet(deltaValues, true); final ChartRenderingInfo deltaInfo = this.createChart(x, y, deltaSet, metricName, deltaChartPath, sc); final String deltaTag = this.createImageMap(deltaInfo, deltaValues, true, "deltaimgMap"); chartValue.setDeltaChart(deltaChartPath); chartValue.setDeltaTag(deltaTag); if (metric.getExtratcsFrom() == Metric.EXTRACTS_FROM_PROJECT) { chartValue.setProjectMetric(true); } else { System.out.println("Average Chart"); List<RevisionMetricValueDto> averageValues = getAverageValue(softwareProject, metric, dataValues); final String avgChartPath = DEFAULT_CHART_PATH + "DynamicChart" + System.currentTimeMillis() + ".png"; XYSeriesCollection avgset = createDataSet(averageValues, false); final ChartRenderingInfo avgInfo = this.createChart(x, y, avgset, metricName, avgChartPath, sc); final String avgTag = this.createImageMap(avgInfo, averageValues, false, "averageimgMap"); chartValue.setAverageChart(avgChartPath); chartValue.setAverageTag(avgTag); chartValue.setProjectMetric(false); } return chartValue; } public List<Double> getSoftwareProjectMetricMinMaxValues(SoftwareProject softwareProject, Metric metric) { System.out.println("getSoftwareProjectMetricMinMaxValues"); List<RevisionMetricValueDto> dataValues = getDataValues(softwareProject, metric); double maxValue = dataValues.get(0).getAbsoluteMetricValue(); double minValue = maxValue; for (RevisionMetricValueDto mv : dataValues) { minValue = getMinValue(mv, minValue); maxValue = getMaxValue(mv, maxValue); } List<Double> minMax = new ArrayList<Double>(2); minMax.add(minValue); minMax.add(maxValue); return minMax; } // public double getSoftwareProjectMetricMaxValue(SoftwareProject softwareProject, Metric metric) { // List<RevisionMetricValueDto> dataValues = getDataValues(softwareProject, metric); // double maxValue = dataValues.get(0).getAbsoluteMetricValue(); // for (RevisionMetricValueDto mv : dataValues) { // maxValue = getMaxValue(mv, maxValue); // } // return maxValue; // } // // public double getSoftwareProjectMetricMinValue(SoftwareProject softwareProject, Metric metric) { // List<RevisionMetricValueDto> dataValues = getDataValues(softwareProject, metric); // double minValue = dataValues.get(0).getAbsoluteMetricValue(); // for (RevisionMetricValueDto mv : dataValues) { // minValue = getMinValue(mv, minValue); // } // return minValue; // } private double getMaxValue(RevisionMetricValueDto mv, double currentMaxValue) { if (!Double.isNaN(mv.getAbsoluteMetricValue())) { if ((mv.getAbsoluteMetricValue() > currentMaxValue) || (Double.isNaN(currentMaxValue))) { return mv.getAbsoluteMetricValue(); } } return currentMaxValue; } private double getMinValue(RevisionMetricValueDto mv, double currentMinValue) { if (!Double.isNaN(mv.getAbsoluteMetricValue())) { if ((mv.getAbsoluteMetricValue() < currentMinValue) || (Double.isNaN(currentMinValue))) { return mv.getAbsoluteMetricValue(); } } return currentMinValue; } public ChartValue getHistogramValue(SoftwareProject softwareProject, Metric metric, int numberOfSets, double doubleHistogram[], int x, int y, ServletContext sc) { System.out.println("getHistogramValue"); List<RevisionMetricValueDto> dataValues = getDataValues(softwareProject, metric); boolean isDelta = false; XIntervalSeriesCollection dataset = createHistogramDataSet(dataValues, numberOfSets, doubleHistogram, isDelta); System.out.println("criando histograma"); String metricName = metric.getName(); String chartPath = DEFAULT_CHART_PATH + "DynamicHistogram" + System.currentTimeMillis() + ".png"; ChartRenderingInfo info = this.createHistogram(x, y, dataset, metricName, chartPath, sc); String tag = this.createHistogramImageMap(info); System.out.println("terminado histograma"); ChartValue chartValue = new ChartValue(); chartValue.setTag(tag); chartValue.setChartPath(chartPath); return chartValue; } private XIntervalSeriesCollection createHistogramDataSet(List<RevisionMetricValueDto> dataValues, int numberOfSets, double doubleHistogram[], boolean isDelta) { XIntervalSeriesCollection dataset = new XIntervalSeriesCollection(); XIntervalSeries series = new XIntervalSeries("Metric Values"); int y[] = new int[numberOfSets + 1]; int i; for (i = 0; i < (numberOfSets + 1); i++) { y[i] = 0; } double auxValue; for (RevisionMetricValueDto dataValue : dataValues) { if (isDelta) { auxValue = dataValue.getDeltaMetricValue(); } else { auxValue = dataValue.getAbsoluteMetricValue(); } if (!Double.isNaN(auxValue)) { i = this.getIndexOfHistogram(auxValue, numberOfSets, doubleHistogram); if (i >= 0) { y[i] = y[i] + 1; } } } for (i = 0; i < (numberOfSets); i++) { series.add((doubleHistogram[i] + doubleHistogram[i + 1]) / 2, doubleHistogram[i], doubleHistogram[i + 1], y[i]); } dataset.addSeries(series); return dataset; } private int getIndexOfHistogram(double auxValue, int numberOfSets, double doubleHistogram[]) { int i = -1; int j = 0; if (auxValue == doubleHistogram[numberOfSets]) { i = numberOfSets; } else { while (j < (numberOfSets) && i < 0) { if ((auxValue >= doubleHistogram[j]) && (auxValue < doubleHistogram[j + 1])) { i = j; } j++; } } return i; } private ChartRenderingInfo createHistogram(int x, int y, XIntervalSeriesCollection dataset, String metricName, String chartPath, ServletContext sc) { try { JFreeChart chart = ChartFactory.createXYBarChart(metricName + " Histogram", "Value", false, "Number", dataset, PlotOrientation.VERTICAL, false, true, false); File chartFile = new File(sc.getRealPath(chartPath)); ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection()); ChartUtilities.saveChartAsPNG(chartFile, chart, x, y, info); return info; } catch (Exception e) { return null; } } private String createHistogramImageMap(ChartRenderingInfo info) { return ChartUtilities.getImageMap("imgHistogramMap", info); } private XYSeriesCollection createDataSet(List<RevisionMetricValueDto> dataValues, boolean isDelta) { XYSeriesCollection dataset = new XYSeriesCollection(); XYSeries principalSeries = null; System.out.println("isDelta = " + isDelta); if (isDelta) { principalSeries = new XYSeries("Delta Values"); } else { principalSeries = new XYSeries("Absolute Values"); } Number x, y; int index = 1; Long firstDay = null; for (RevisionMetricValueDto dataValue : dataValues) { x = Long.parseLong(dataValue.getRevisionNumber()); if (isDelta) { y = dataValue.getDeltaMetricValue(); } else { y = dataValue.getAbsoluteMetricValue(); } if (USE_RELATIVE_REVISION_NUMBER) { principalSeries.add(index, y); } else if (USE_RELATIVE_COMMIT_DATE) { if (firstDay == null) { firstDay = dataValue.getCommitDate().getTimeInMillis(); } long relativeDayOfDevelopment = Math.round(Math.floor((dataValue.getCommitDate().getTimeInMillis() - firstDay + 1) / 60000)); principalSeries.add(relativeDayOfDevelopment, y); } else { principalSeries.add(x, y); } index++; } if (dataValues.size() > 0) { final Number firstRevisionNumber, lastRevisionNumber; firstRevisionNumber = principalSeries.getMinX(); lastRevisionNumber = principalSeries.getMaxX(); XYSeries averageSeries = new XYSeries("Average"); double average = calculateAverage(dataValues, isDelta); averageSeries.add(firstRevisionNumber, average); averageSeries.add(lastRevisionNumber, average); XYSeries positiveSDSeries = new XYSeries("Stardard Deviation +"); XYSeries negativeSDSeries = new XYSeries("Stardard Deviation -"); double standardDerivation = calculateStandardDerivation(dataValues, average, isDelta); positiveSDSeries.add(firstRevisionNumber, average + standardDerivation); positiveSDSeries.add(lastRevisionNumber, average + standardDerivation); negativeSDSeries.add(firstRevisionNumber, average - standardDerivation); negativeSDSeries.add(lastRevisionNumber, average - standardDerivation); XYSeries positiveSDSeries3 = new XYSeries("Stardard Deviation + (3)"); XYSeries negativeSDSeries3 = new XYSeries("Stardard Deviation - (3)"); positiveSDSeries3.add(firstRevisionNumber, average + (3 * standardDerivation)); positiveSDSeries3.add(lastRevisionNumber, average + (3 * standardDerivation)); negativeSDSeries3.add(firstRevisionNumber, average - (3 * standardDerivation)); negativeSDSeries3.add(lastRevisionNumber, average - (3 * standardDerivation)); System.out.println("average: " + average); System.out.println("stardarddeviation: " + standardDerivation); System.out.println("firstRevision: " + firstRevisionNumber); System.out.println("lasRevisation: " + lastRevisionNumber); dataset.addSeries(principalSeries); dataset.addSeries(averageSeries); dataset.addSeries(positiveSDSeries); dataset.addSeries(negativeSDSeries); dataset.addSeries(positiveSDSeries3); dataset.addSeries(negativeSDSeries3); } return dataset; } private ChartRenderingInfo createChart(int x, int y, XYSeriesCollection dataset, String metricName, String chartPath, ServletContext sc) { try { final String xAxisLabel = (USE_RELATIVE_COMMIT_DATE ? "Development Time (min)" : "Number of commit"); JFreeChart chart = ChartFactory.createXYLineChart(metricName, xAxisLabel, "Value", dataset, PlotOrientation.VERTICAL, false, true, false); XYPlot plot = chart.getXYPlot(); plot.getRenderer().setSeriesPaint(0, Color.BLUE); plot.getRenderer().setSeriesPaint(1, Color.GREEN); plot.getRenderer().setSeriesPaint(2, Color.YELLOW); plot.getRenderer().setSeriesPaint(3, Color.YELLOW); plot.getRenderer().setSeriesPaint(4, Color.RED); plot.getRenderer().setSeriesPaint(5, Color.RED); Double minY = null; Double maxY = null; // System.out.println("minY = " + minY); // System.out.println("maxY = " + maxY); for (Object object : dataset.getSeries()) { XYSeries series = (XYSeries) object; if (minY == null || series.getMinY() < minY) { minY = series.getMinY(); // System.out.println("minY = " + minY); } if (maxY == null || series.getMaxY() > maxY) { // System.out.println("maxY = " + maxY); maxY = series.getMaxY(); } } // System.out.println("minY = " + minY); // System.out.println("maxY = " + maxY); if (minY != null && maxY != null) { minY -= minY * 0.1; maxY += maxY * 0.1; plot.getRangeAxis().setRange(minY, maxY); } // System.out.println("sc.getRealPath() = " + sc.getRealPath("")); File chartFile = new File(sc.getRealPath(chartPath)); ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection()); ChartUtilities.saveChartAsPNG(chartFile, chart, x, y, info); //chartImage = new DefaultStreamedContent(new FileInputStream(chartFile), "image/png"); // System.out.println("chartFile.getAbsolutePath() = " + chartFile.getAbsolutePath()); return info; } catch (IOException ex) { ex.printStackTrace(); } return null; } private String createImageMap(ChartRenderingInfo info, List<RevisionMetricValueDto> dataValues, boolean isDelta, String nameMap) { EntityCollection newEntityCollection = new StandardEntityCollection(); EntityCollection entityCollection = info.getEntityCollection(); Iterator iteratorEntityCollection = entityCollection.iterator(); String text; ChartEntity chartEntity; while (iteratorEntityCollection.hasNext()) { chartEntity = (ChartEntity) iteratorEntityCollection.next(); text = chartEntity.getClass().toString(); if (text.equals("class org.jfree.chart.entity.XYItemEntity")) { if (((XYItemEntity) chartEntity).getSeriesIndex() == 0) { RevisionMetricValueDto dataValue = (RevisionMetricValueDto) dataValues.get(((XYItemEntity) chartEntity).getItem()); double metricValue = (isDelta ? dataValue.getDeltaMetricValue() : dataValue.getAbsoluteMetricValue()); String commitDate = ""; if (dataValue.getCommitDate() == null) { System.out.println("dataValue.getCommitDate() = " + dataValue.getCommitDate() + " revision: " + dataValue.getRevisionNumber() + " : " + dataValue); } else { commitDate = DateUtil.format(dataValue.getCommitDate()); } chartEntity.setToolTipText("Revision: " + dataValue.getRevisionNumber() + " - Value: " + metricValue + " - Commiter: " + dataValue.getCommiter() + " - Commit Date: " + commitDate); } } newEntityCollection.add(chartEntity); } info.setEntityCollection(newEntityCollection); String tag = null; tag = ChartUtilities.getImageMap(nameMap, info); return tag; } private double calculateAverage(List<RevisionMetricValueDto> dataValues, boolean isDelta) { double average = 0; double value; int count = 0; for (RevisionMetricValueDto dataValue : dataValues) { //average += (isDelta ? dataValue.getDeltaMetricValue() : dataValue.getAbsoluteMetricValue()); value = (isDelta ? dataValue.getDeltaMetricValue() : dataValue.getAbsoluteMetricValue()); if (!Double.isNaN(value)) { average += value; count++; } System.out.println("Revisao: " + dataValue.getRevisionNumber() + " valor: " + (isDelta ? dataValue.getDeltaMetricValue() : dataValue.getAbsoluteMetricValue())); } if (count != 0) { average /= count; } return average; } private double calculateStandardDerivation(List<RevisionMetricValueDto> RevisionMetricValueDtos, double average, boolean isDelta) { double x; double total = 0; double standardDerivation; int count = 0; for (RevisionMetricValueDto dataValue : RevisionMetricValueDtos) { x = (isDelta ? dataValue.getDeltaMetricValue() : dataValue.getAbsoluteMetricValue()) - average; if (!Double.isNaN(x)) { x = x * x; total = total + x; count++; } } double count2 = count - 1; standardDerivation = (1.0 / (count2)); standardDerivation = standardDerivation * total; standardDerivation = Math.sqrt(standardDerivation); return standardDerivation; } private List<RevisionMetricValueDto> getAverageValue(SoftwareProject softwareProject, Metric metric, List<RevisionMetricValueDto> dataValues) { System.out.println("getAverageValue"); if (softwareProject == softwareProjectBuffer && metric == metricBuffer) { if ( averageValuesBuffer != null) { System.out.println("returning buffer"); return this.averageValuesBuffer; } } else { resetBuffers(softwareProject, metric); } //Average is for non project metrics if (metric.getExtratcsFrom() == Metric.EXTRACTS_FROM_PROJECT) { return null; } System.out.println("extracting average values"); this.averageValuesBuffer = new ArrayList<RevisionMetricValueDto>(dataValues.size()); for (RevisionMetricValueDto dt : dataValues) { final double averageValue; if (dt.getCountItems() == null || dt.getCountItems().equals("N/A")) { averageValue = Double.NaN; } else { averageValue = dt.getAbsoluteMetricValue() / Double.parseDouble(dt.getCountItems()); } RevisionMetricValueDto newDataValue = new RevisionMetricValueDto(dt.getRevisionNumber(), dt.getCommiter(), dt.getCountItems(), dt.getCommitDate(), averageValue, 0D); this.averageValuesBuffer.add(newDataValue); } return this.averageValuesBuffer; } private List<RevisionMetricValueDto> getDeltaValues(SoftwareProject softwareProject, Metric metric) { if (softwareProject == softwareProjectBuffer && metric == metricBuffer) { if (deltaValuesBuffer != null) { System.out.println("returning buffer"); return this.deltaValuesBuffer; } } else { resetBuffers(softwareProject, metric); } System.out.println("extracting delta values"); List<MetricValue> deltaMetricValues = metricValueService.getDeltaValuesByProjectAndMetric(softwareProject, metric); deltaValuesBuffer = new ArrayList<RevisionMetricValueDto>(deltaMetricValues.size()); for (MetricValue metricValue : deltaMetricValues) { //System.out.println("delta metricValue = " + metricValue); deltaValuesBuffer.add(RevisionMetricValueDto.createFromMetricValue(metricValue)); } //System.out.println("tamanho: " + deltaMetricValues.size()); return deltaValuesBuffer; } private List<RevisionMetricValueDto> getDataValues(SoftwareProject softwareProject, Metric metric) { System.out.println("getDataValues"); if (softwareProject == softwareProjectBuffer && metric == metricBuffer) { if (dataValuesBuffer != null) { System.out.println("returning buffer"); return this.dataValuesBuffer; } } else { resetBuffers(softwareProject, metric); } System.out.println("extracting data values"); if (metric.getExtratcsFrom() == Metric.EXTRACTS_FROM_PROJECT) { List<MetricValue> absoluteMetricValues = metricValueService.getAbsoluteValuesByProjectAndMetric(softwareProject, metric); dataValuesBuffer = new ArrayList<RevisionMetricValueDto>(absoluteMetricValues.size()); for (MetricValue metricValue : absoluteMetricValues) { dataValuesBuffer.add(RevisionMetricValueDto.createFromMetricValue(metricValue)); } } else { dataValuesBuffer = ostraMetricValueService.getProjectMetricsToDetail(softwareProject, metric.getName()); for (RevisionMetricValueDto mv : dataValuesBuffer) { if (mv.getSumMetricValue().equals("N/A")) { mv.setAbsoluteMetricValue(Double.NaN); } else { mv.setAbsoluteMetricValue(Double.parseDouble(mv.getSumMetricValue())); } } } return dataValuesBuffer; } private void resetBuffers(SoftwareProject softwareProject, Metric metric) { System.out.println("resetBuffers"); this.softwareProjectBuffer = softwareProject; this.metricBuffer = metric; this.averageValuesBuffer = null; this.deltaValuesBuffer = null; this.dataValuesBuffer = null; } }