// Copyright 2012 Georg-August-Universität Göttingen, Germany // // 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 under the License. package de.ugoe.cs.autoquest.ui.swt; import java.awt.BasicStroke; import java.awt.Graphics2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.text.DecimalFormat; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.LogarithmicAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.title.TextTitle; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.ui.RectangleAnchor; import org.jfree.ui.RectangleInsets; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.DefaultFontMapper; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfTemplate; import com.lowagie.text.pdf.PdfWriter; import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskTreeUtils; /** *

* TODO comment *

* * @author Patrick Harms */ public class TaskTreePlotUtils { /** name for visualizing a task count to event coverage plot */ public static final String TASK_COUNT__EVENT_COVERAGE_PLOT = "event coverage plot"; /** name for visualizing the instance counts of tasks ordered by the event coverage */ public static final String ORDERED_TASK_COUNT_PLOT = "task instance count - coverage plot"; /** name for visualizing a cumulative coverage of events by the tasks orded by their event * coverage */ public static final String CUMULATIVE_TASK_COVERAGE_PLOT = "cumulative task coverage plot"; /** colors */ public static final java.awt.Color[] colors = new java.awt.Color[] { java.awt.Color.BLACK, java.awt.Color.GRAY, java.awt.Color.RED, java.awt.Color.CYAN, java.awt.Color.ORANGE, java.awt.Color.GREEN, java.awt.Color.MAGENTA, java.awt.Color.BLUE, java.awt.Color.PINK, java.awt.Color.DARK_GRAY, java.awt.Color.LIGHT_GRAY, java.awt.Color.YELLOW }; /** * */ public static JFreeChart createPlot(String name, List models, List modelNames, List groups) { JFreeChart chart = null; if (TASK_COUNT__EVENT_COVERAGE_PLOT.equals(name)) { XYDataset dataset1 = createTaskCountEventCoveragePlotDataSet(models.get(0), modelNames.get(0)); chart = ChartFactory.createXYLineChart (TASK_COUNT__EVENT_COVERAGE_PLOT, "Sequences ordered by coverage in % of sequences", "action instances in % of all recorded action instances covered by the sequence", dataset1, PlotOrientation.VERTICAL, models.size() > 1, false, false); for (int i = 1; i < models.size(); i++) { chart.getXYPlot().setDataset (i, createTaskCountEventCoveragePlotDataSet(models.get(i), modelNames.get(i))); } XYPlot plot = chart.getXYPlot(); plot.setBackgroundPaint(java.awt.Color.WHITE); plot.setDomainAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); XYLineAndShapeRenderer scatterRenderer = new XYLineAndShapeRenderer(false, true); scatterRenderer.setSeriesShapesFilled(0, false); scatterRenderer.setSeriesShape(0, new Ellipse2D.Float(-3.0f, -3.0f, 6.0f, 6.0f)); scatterRenderer.setSeriesPaint(0, colors[0]); plot.setRenderer(0, scatterRenderer); for (int i = 1; i < models.size(); i++) { chart.getXYPlot().setDataset (i, createOrderedTaskCountPlotDataSet(models.get(i), modelNames.get(i))); scatterRenderer = new XYLineAndShapeRenderer(false, true); scatterRenderer.setSeriesShapesFilled(0, false); scatterRenderer.setSeriesShape(0, new Ellipse2D.Float(-3.0f, -3.0f, 6.0f, 6.0f)); scatterRenderer.setSeriesPaint(0, colors[i]); plot.setRenderer(i, scatterRenderer); } NumberAxis domainAxis = new NumberAxis ("Sequences ordered by coverage in % of sequences"); domainAxis.setUpperBound(100); plot.setDomainAxis(domainAxis); NumberAxis rangeAxis = new LogarithmicAxis ("action instances in % of all recorded action instances covered by the sequence"); rangeAxis.setAutoRange(true); plot.setRangeAxis(rangeAxis); } else if (CUMULATIVE_TASK_COVERAGE_PLOT.equals(name)) { Set mostProminent = TaskTreeUtils.getMostProminentTasks(models.get(0), models.get(0).getTasks()); XYDataset dataset = createCumulativeTaskCoveragePlotDataSet(models.get(0), modelNames.get(0)); chart = ChartFactory.createXYLineChart (CUMULATIVE_TASK_COVERAGE_PLOT, "Sequences ordered by coverage in % of sequences", "Cumulative action instance coverage in % of all recorded action instances", dataset, PlotOrientation.VERTICAL, models.size() > 1, false, false); final XYPlot plot = chart.getXYPlot(); plot.setBackgroundPaint(java.awt.Color.WHITE); plot.setDomainAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); double mpMarker = (double) 100 * mostProminent.size() / dataset.getItemCount(0); ValueMarker marker = new ValueMarker(mpMarker); marker.setLabel(new DecimalFormat("#0.0").format(mpMarker)); marker.setLabelAnchor(RectangleAnchor.BOTTOM); marker.setLabelOffset(new RectangleInsets(0, -12, 10, 15)); plot.addDomainMarker(marker); double yMP = dataset.getYValue(0, mostProminent.size() - 1); double maxY = dataset.getYValue(0, dataset.getItemCount(0) - 1); marker = new ValueMarker(yMP); marker.setLabel(new DecimalFormat("#0.0").format(yMP)); marker.setLabelOffset(new RectangleInsets(5, 15, 0, 10)); plot.addRangeMarker(marker); marker = new ValueMarker(maxY); marker.setLabel(new DecimalFormat("#0.0").format(maxY)); marker.setLabelOffset(new RectangleInsets(5, 15, 0, 10)); plot.addRangeMarker(marker); plot.getRenderer(0).setSeriesPaint(0, colors[0]); plot.getRenderer(0).setSeriesStroke(0, new BasicStroke(3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); for (int i = 1; i < models.size(); i++) { plot.setDataset(i, createCumulativeTaskCoveragePlotDataSet(models.get(i), modelNames.get(i))); plot.setRenderer(i, new XYLineAndShapeRenderer(true, false)); plot.getRenderer(i).setSeriesPaint(0, colors[groups.get(i)]); plot.getRenderer(i).setSeriesStroke(0, new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); } NumberAxis domainAxis = new NumberAxis ("Sequences ordered by coverage in % of sequences"); domainAxis.setUpperBound(100); plot.setDomainAxis(domainAxis); NumberAxis rangeAxis = new NumberAxis ("Cumulative action instance coverage in % of all recorded action instances"); rangeAxis.setUpperBound(100); plot.setRangeAxis(rangeAxis); } else if (ORDERED_TASK_COUNT_PLOT.equals(name)) { XYDataset dataset = createOrderedTaskCountPlotDataSet(models.get(0), modelNames.get(0)); chart = ChartFactory.createXYLineChart (ORDERED_TASK_COUNT_PLOT, "tasks ordered by coverage", "number of instances", dataset, PlotOrientation.VERTICAL, models.size() > 1, false, false); final XYPlot plot = chart.getXYPlot(); plot.setBackgroundPaint(java.awt.Color.WHITE); plot.setDomainAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); XYLineAndShapeRenderer scatterRenderer = new XYLineAndShapeRenderer(false, true); scatterRenderer.setSeriesShapesFilled(0, false); scatterRenderer.setSeriesShape(0, new Ellipse2D.Float(-3.0f, -3.0f, 6.0f, 6.0f)); scatterRenderer.setSeriesPaint(0, colors[0]); plot.setRenderer(0, scatterRenderer); for (int i = 1; i < models.size(); i++) { chart.getXYPlot().setDataset (i, createOrderedTaskCountPlotDataSet(models.get(i), modelNames.get(i))); scatterRenderer = new XYLineAndShapeRenderer(false, true); scatterRenderer.setSeriesShapesFilled(0, false); scatterRenderer.setSeriesShape(0, new Ellipse2D.Float(-3.0f, -3.0f, 6.0f, 6.0f)); scatterRenderer.setSeriesPaint(0, colors[i]); plot.setRenderer(i, scatterRenderer); } NumberAxis domainAxis = new NumberAxis("sequences ordered by coverage"); domainAxis.setUpperBound(100); plot.setDomainAxis(domainAxis); NumberAxis rangeAxis = new LogarithmicAxis("number of instances"); plot.setRangeAxis(rangeAxis); } return chart; } /** * */ public static void saveChartToPDF(JFreeChart chart, String fileName, int width, int height) throws Exception { if (chart != null) { BufferedOutputStream out = null; try { out = new BufferedOutputStream(new FileOutputStream(fileName)); //convert chart to PDF with iText: Rectangle pagesize = new Rectangle(width, height); com.lowagie.text.Document document = new com.lowagie.text.Document(pagesize, 50, 50, 50, 50); try { PdfWriter writer = PdfWriter.getInstance(document, out); document.addAuthor("JFreeChart"); document.open(); PdfContentByte cb = writer.getDirectContent(); PdfTemplate tp = cb.createTemplate(width, height); Graphics2D g2 = tp.createGraphics(width, height, new DefaultFontMapper()); Rectangle2D r2D = new Rectangle2D.Double(0, 0, width, height); TextTitle title = chart.getTitle(); chart.setTitle((TextTitle) null); chart.draw(g2, r2D, null); chart.setTitle(title); g2.dispose(); cb.addTemplate(tp, 0, 0); } finally { document.close(); } } finally { if (out != null) { out.close(); } } } } /** * */ private static XYDataset createTaskCountEventCoveragePlotDataSet(ITaskModel taskModel, String taskModelName) { Map> coverageCounts = new HashMap<>(); Map> coverages = new HashMap<>(); Set allEvents = new HashSet<>(); int maxCoverage = 0; for (ITask task : taskModel.getTasks()) { if (task instanceof ISequence) { final Set coveredEvents = new HashSet<>(); for (ITaskInstance instance : task.getInstances()) { instance.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { coveredEvents.add(eventTaskInstance); } }); } coverages.put((ISequence) task, coveredEvents); List tasksWithSameCoverage = coverageCounts.get(coveredEvents.size()); if (tasksWithSameCoverage == null) { tasksWithSameCoverage = new LinkedList<>(); coverageCounts.put(coveredEvents.size(), tasksWithSameCoverage); } tasksWithSameCoverage.add((ISequence) task); maxCoverage = Math.max(maxCoverage, coveredEvents.size()); } else if (task instanceof IEventTask) { for (ITaskInstance instance : task.getInstances()) { allEvents.add((IEventTaskInstance) instance); } } } XYSeries countSeries = new XYSeries(taskModelName, true, false); // entries are correctly sorted due to the map int sequenceIndex = 1; for (int i = maxCoverage; i > 0; i--) { List sequencesWithSameCoverage = coverageCounts.get(i); if (sequencesWithSameCoverage == null) { continue; } for (ISequence sequence : sequencesWithSameCoverage) { double xvalue = (double) 100 * sequenceIndex / coverages.size(); double yvalue = (double) 100 * coverages.get(sequence).size() / allEvents.size(); countSeries.add(xvalue, yvalue); sequenceIndex++; } } DefaultTableXYDataset dataset = new DefaultTableXYDataset(); dataset.addSeries(countSeries); return dataset; } /** * */ private static XYDataset createCumulativeTaskCoveragePlotDataSet(ITaskModel taskModel, String taskModelName) { Map> coverageCounts = new HashMap<>(); Map> coverages = new HashMap<>(); Set allEvents = new HashSet<>(); int maxCoverage = 0; for (ITask task : taskModel.getTasks()) { if (task instanceof ISequence) { final Set coveredEvents = new HashSet<>(); for (ITaskInstance instance : task.getInstances()) { instance.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { coveredEvents.add(eventTaskInstance); } }); } coverages.put((ISequence) task, coveredEvents); List tasksWithSameCoverage = coverageCounts.get(coveredEvents.size()); if (tasksWithSameCoverage == null) { tasksWithSameCoverage = new LinkedList<>(); coverageCounts.put(coveredEvents.size(), tasksWithSameCoverage); } tasksWithSameCoverage.add((ISequence) task); maxCoverage = Math.max(maxCoverage, coveredEvents.size()); } else if (task instanceof IEventTask) { for (ITaskInstance instance : task.getInstances()) { allEvents.add((IEventTaskInstance) instance); } } } XYSeries cumulativeSeries = new XYSeries(taskModelName, true, false); Set cumulativeCoverage = new HashSet<>(); // entries are correctly sorted due to the map int sequenceIndex = 1; for (int i = maxCoverage; i > 0; i--) { List sequencesWithSameCoverage = coverageCounts.get(i); if (sequencesWithSameCoverage == null) { continue; } for (ISequence sequence : sequencesWithSameCoverage) { cumulativeCoverage.addAll(coverages.get(sequence)); double xvalue = (double) 100 * sequenceIndex / coverages.size(); double yvalue = (double) 100 * cumulativeCoverage.size() / allEvents.size(); cumulativeSeries.add(xvalue, yvalue); sequenceIndex++; } } DefaultTableXYDataset dataset = new DefaultTableXYDataset(); dataset.addSeries(cumulativeSeries); return dataset; } /** * */ private static XYDataset createOrderedTaskCountPlotDataSet(ITaskModel taskModel, String taskModelName) { Map> coverageCounts = new HashMap<>(); Map> coverages = new HashMap<>(); int maxCoverage = 0; for (ITask task : taskModel.getTasks()) { if (task instanceof ISequence) { final Set coveredEvents = new HashSet<>(); for (ITaskInstance instance : task.getInstances()) { instance.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { coveredEvents.add(eventTaskInstance); } }); } coverages.put((ISequence) task, coveredEvents); List tasksWithSameCoverage = coverageCounts.get(coveredEvents.size()); if (tasksWithSameCoverage == null) { tasksWithSameCoverage = new LinkedList<>(); coverageCounts.put(coveredEvents.size(), tasksWithSameCoverage); } tasksWithSameCoverage.add((ISequence) task); maxCoverage = Math.max(maxCoverage, coveredEvents.size()); } } XYSeries counts = new XYSeries(taskModelName, true, false); // entries are correctly sorted due to the map int sequenceIndex = 1; for (int i = maxCoverage; i > 0; i--) { List sequencesWithSameCoverage = coverageCounts.get(i); if (sequencesWithSameCoverage == null) { continue; } for (ISequence sequence : sequencesWithSameCoverage) { double xvalue = (double) 100 * sequenceIndex / coverages.size(); counts.add(xvalue, sequence.getInstances().size()); sequenceIndex++; } } DefaultTableXYDataset dataset = new DefaultTableXYDataset(); dataset.addSeries(counts); return dataset; } }