package rocks.inspectit.ui.rcp.editor.graph.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.geom.Ellipse2D; import java.text.DateFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.DeviationRenderer; import org.jfree.chart.renderer.xy.XYBarRenderer; import org.jfree.data.RangeType; import org.jfree.data.time.Millisecond; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.xy.XYBarDataset; import org.jfree.data.xy.YIntervalSeriesCollection; import org.jfree.ui.RectangleInsets; import rocks.inspectit.shared.all.communication.data.TimerData; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId; import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition; /** * Abstract plot controller for all graphs concerning the timer data and it's sub-classes. * * @author Ivan Senic * * @param <E> * Type of template */ public abstract class AbstractTimerDataPlotController<E extends TimerData> extends AbstractPlotController { /** * Colors we will use for series. */ private static final int[] SERIES_COLORS = new int[] { SWT.COLOR_RED, SWT.COLOR_BLUE, SWT.COLOR_DARK_GREEN, SWT.COLOR_DARK_YELLOW, SWT.COLOR_DARK_GRAY, SWT.COLOR_BLACK, SWT.COLOR_DARK_CYAN, SWT.COLOR_DARK_BLUE }; /** * The map containing the weight of the {@link XYPlot}s. */ private Map<XYPlot, Integer> weights = new HashMap<>(); /** * Plot used to display duration of the HTTP requests. */ private XYPlot durationPlot; /** * Plot used to display the count of the HTTP requests. */ private XYPlot countPlot; /** * Duration series. */ private List<YIntervalSeriesImproved> durationSeries; /** * Count series. */ private List<TimeSeries> countSeries; /** * Returns series key for given template. * * @param template * Template * @return {@link Comparable} representing series key. */ protected abstract Comparable<?> getSeriesKey(E template); /** * Returns the template list. Each object in this list will represent one series. * * @return Returns the template list. */ protected abstract List<E> getTemplates(); /** * {@inheritDoc} */ @Override public Set<PreferenceId> getPreferenceIds() { Set<PreferenceId> preferenceIds = EnumSet.noneOf(PreferenceId.class); if (getInputDefinition().getRepositoryDefinition() instanceof CmrRepositoryDefinition) { preferenceIds.add(PreferenceId.LIVEMODE); } preferenceIds.add(PreferenceId.TIMELINE); preferenceIds.add(PreferenceId.SAMPLINGRATE); preferenceIds.add(PreferenceId.UPDATE); return preferenceIds; } /** * {@inheritDoc} */ @Override public List<XYPlot> getPlots() { durationPlot = initializeDurationPlot(); countPlot = initializeCountPlot(); weights.put(durationPlot, 2); weights.put(countPlot, 1); List<XYPlot> list = new ArrayList<>(); Collections.addAll(list, durationPlot, countPlot); return list; } /** * {@inheritDoc} */ @Override public int getWeight(XYPlot subPlot) { return weights.get(subPlot); } /** * Removes all data from the upper plot and sets the {@link TimerData} objects on the plot. * * @param map * The data to set on the plot. */ protected void setDurationPlotData(Map<Object, List<E>> map) { for (YIntervalSeriesImproved series : durationSeries) { series.clear(); for (Entry<Object, List<E>> entry : map.entrySet()) { if (series.getKey().equals(entry.getKey())) { for (E data : entry.getValue()) { series.add(data.getTimeStamp().getTime(), data.getAverage(), data.getMin(), data.getMax(), false); } break; } } series.fireSeriesChanged(); } } /** * Removes all data from the upper plot and sets the {@link TimerData} objects on the plot. * * @param map * The data to set on the plot. */ protected void setCountPlotData(Map<Object, List<E>> map) { for (TimeSeries series : countSeries) { series.clear(); series.setNotify(false); for (Entry<Object, List<E>> entry : map.entrySet()) { if (series.getKey().equals(entry.getKey())) { for (E data : entry.getValue()) { series.addOrUpdate(new Millisecond(data.getTimeStamp()), data.getCount()); } break; } } series.setNotify(true); series.fireSeriesChanged(); } } /** * Initializes the duration plot. * * @return An instance of {@link XYPlot}. */ private XYPlot initializeDurationPlot() { Set<Comparable<?>> keys = new HashSet<>(); durationSeries = new ArrayList<>(); YIntervalSeriesCollection yintervalseriescollection = new YIntervalSeriesCollection(); for (E template : getTemplates()) { Comparable<?> seriesKey = getSeriesKey(template); if (keys.add(seriesKey)) { YIntervalSeriesImproved yIntervalSeries = new YIntervalSeriesImproved(seriesKey); yintervalseriescollection.addSeries(yIntervalSeries); durationSeries.add(yIntervalSeries); } } DeviationRenderer renderer = new DeviationRenderer(true, false); renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT, DateFormat.getDateTimeInstance(), NumberFormat.getNumberInstance())); renderer.setBaseShapesVisible(true); renderer.setAlpha(0.1f); Display display = Display.getDefault(); for (int i = 0; i < durationSeries.size(); i++) { int color = SERIES_COLORS[i % SERIES_COLORS.length]; RGB rgb = display.getSystemColor(color).getRGB(); renderer.setSeriesStroke(i, new BasicStroke(3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); renderer.setSeriesFillPaint(i, new Color(rgb.red, rgb.green, rgb.blue)); renderer.setSeriesOutlineStroke(i, new BasicStroke(2.0f)); renderer.setSeriesShape(i, new Ellipse2D.Double(-2.5, -2.5, 5.0, 5.0)); } NumberAxis rangeAxis = new NumberAxis("ms"); rangeAxis.setAutoRangeMinimumSize(100.0d); rangeAxis.setRangeType(RangeType.POSITIVE); rangeAxis.setAutoRangeIncludesZero(true); XYPlot subplot = new XYPlot(yintervalseriescollection, null, rangeAxis, renderer); subplot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0)); subplot.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false); subplot.setRangeCrosshairVisible(true); return subplot; } /** * Initializes the lower plot. * * @return An instance of {@link XYPlot} */ private XYPlot initializeCountPlot() { Set<Comparable<?>> keys = new HashSet<>(); countSeries = new ArrayList<>(); TimeSeriesCollection dataset = new TimeSeriesCollection(); for (E template : getTemplates()) { Comparable<?> seriesKey = getSeriesKey(template); if (keys.add(seriesKey)) { TimeSeries timeSeries = new TimeSeries(seriesKey); countSeries.add(timeSeries); dataset.addSeries(timeSeries); } } // ISE: No idea why we have 30 here, used same value as in other charts XYBarDataset ds = new XYBarDataset(dataset, 30); XYBarRenderer renderer = new XYBarRenderer(); renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT, DateFormat.getDateTimeInstance(), NumberFormat.getNumberInstance())); renderer.setShadowVisible(false); renderer.setMargin(0.1d); Display display = Display.getDefault(); for (int i = 0; i < countSeries.size(); i++) { int color = SERIES_COLORS[i % SERIES_COLORS.length]; RGB rgb = display.getSystemColor(color).getRGB(); renderer.setSeriesPaint(i, new Color(rgb.red, rgb.green, rgb.blue)); renderer.setSeriesVisibleInLegend(i, Boolean.FALSE); } NumberAxis rangeAxis = new NumberAxis("Count"); rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); rangeAxis.setAutoRange(true); rangeAxis.setRangeType(RangeType.POSITIVE); XYPlot subplot = new XYPlot(ds, null, rangeAxis, renderer); subplot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0)); subplot.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT); return subplot; } }