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.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.data.RangeType;
import org.jfree.data.xy.YIntervalSeriesCollection;
import org.jfree.ui.RectangleInsets;
import rocks.inspectit.shared.all.communication.data.ClassLoadingInformationData;
import rocks.inspectit.shared.cs.cmr.service.IGlobalDataAccessService;
import rocks.inspectit.shared.cs.indexing.aggregation.IAggregator;
import rocks.inspectit.shared.cs.indexing.aggregation.impl.ClassLoadingInformationDataAggregator;
import rocks.inspectit.ui.rcp.editor.inputdefinition.InputDefinition;
import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId;
import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition;
/**
* This class creates a {@link XYPlot} containing the {@link ClassLoadingInformationData}
* informations.
*
* @author Eduard Tudenhoefner
* @author Patrice Bouillet
*
*/
public class DefaultClassesPlotController extends AbstractPlotController {
/**
* The ID of this subview / controller.
*/
public static final String ID = "inspectit.subview.graph.classes";
/**
* The template of the {@link ClassLoadingInformationData} object.
*/
private ClassLoadingInformationData template;
/**
* Indicates the weight of the upper {@link XYPlot}.
*/
private static final int WEIGHT_UPPER_PLOT = 1;
/**
* The upper {@link XYPlot} containing the graphical view.
*/
private XYPlot upperPlot;
/**
* The map containing the weights of the {@link XYPlot}s.
*/
private Map<XYPlot, Integer> weights = new HashMap<>();
/**
* The {@link YIntervalSeriesImproved}.
*/
private YIntervalSeriesImproved loadedClasses;
/**
* The {@link YIntervalSeriesImproved}.
*/
private YIntervalSeriesImproved totalLoadedClasses;
/**
* The data access service to access the data on the CMR.
*/
private IGlobalDataAccessService dataAccessService;
/**
* Old list containing some data objects which could be reused.
*/
private List<ClassLoadingInformationData> oldData = Collections.emptyList();
/**
* The old from date.
*/
private Date oldFromDate = new Date(Long.MAX_VALUE);
/**
* The old to date.
*/
private Date oldToDate = new Date(0);
/**
* This represents the date of one of the objects which was received at some time in the past
* but was the one with the newest date. This is needed for not requesting some data of the CMR
* sometimes.
*/
private Date newestDate = new Date(0);
/**
* {@link IAggregator}.
*/
private IAggregator<ClassLoadingInformationData> aggregator;
/**
* {@inheritDoc}
*/
@Override
public void setInputDefinition(InputDefinition inputDefinition) {
super.setInputDefinition(inputDefinition);
template = new ClassLoadingInformationData();
template.setPlatformIdent(inputDefinition.getIdDefinition().getPlatformId());
template.setSensorTypeIdent(inputDefinition.getIdDefinition().getSensorTypeId());
template.setId(-1L);
dataAccessService = inputDefinition.getRepositoryDefinition().getGlobalDataAccessService();
aggregator = new ClassLoadingInformationDataAggregator();
}
/**
* {@inheritDoc}
*/
@Override
public List<XYPlot> getPlots() {
upperPlot = initializeUpperPlot();
List<XYPlot> plots = new ArrayList<>(1);
plots.add(upperPlot);
weights.put(upperPlot, WEIGHT_UPPER_PLOT);
return plots;
}
/**
* Initializes the upper plot.
*
* @return An instance of {@link XYPlot}.
*/
private XYPlot initializeUpperPlot() {
loadedClasses = new YIntervalSeriesImproved("loaded classes");
totalLoadedClasses = new YIntervalSeriesImproved("total loaded classes");
YIntervalSeriesCollection yintervalseriescollection = new YIntervalSeriesCollection();
yintervalseriescollection.addSeries(loadedClasses);
yintervalseriescollection.addSeries(totalLoadedClasses);
DeviationRenderer renderer = new DeviationRenderer(true, false);
renderer.setBaseShapesVisible(true);
renderer.setSeriesStroke(0, new BasicStroke(3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
renderer.setSeriesFillPaint(0, new Color(255, 200, 200));
renderer.setSeriesOutlineStroke(0, new BasicStroke(2.0f));
renderer.setSeriesShape(0, new Ellipse2D.Double(-2.5, -2.5, 5.0, 5.0));
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT, DateFormat.getDateTimeInstance(), NumberFormat.getNumberInstance()));
final NumberAxis rangeAxis = new NumberAxis("Classes");
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
rangeAxis.setAutoRangeMinimumSize(2000.0d);
rangeAxis.setRangeType(RangeType.POSITIVE);
rangeAxis.setAutoRangeIncludesZero(true);
final 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);
subplot.setRangeCrosshairVisible(true);
return subplot;
}
/**
* Updates the upper plot with the given input data.
*
* @param classLoadingData
* The input data.
*/
private void addUpperPlotData(List<ClassLoadingInformationData> classLoadingData) {
for (ClassLoadingInformationData data : classLoadingData) {
int loadedClassAverage = data.getTotalLoadedClassCount() / data.getCount();
long totalLoadedClassAverage = data.getTotalTotalLoadedClassCount() / data.getCount();
loadedClasses.add(data.getTimeStamp().getTime(), loadedClassAverage, data.getMinLoadedClassCount(), data.getMaxLoadedClassCount(), false);
totalLoadedClasses.add(data.getTimeStamp().getTime(), totalLoadedClassAverage, data.getMinTotalLoadedClassCount(), data.getMaxTotalLoadedClassCount(), false);
}
loadedClasses.fireSeriesChanged();
totalLoadedClasses.fireSeriesChanged();
}
/**
* Removes all data from the upper plot and sets the {@link ClassLoadingInformationData} objects
* on the plot.
*
* @param classLoadingData
* The data to set on the plot.
*/
private void setUpperPlotData(List<ClassLoadingInformationData> classLoadingData) {
loadedClasses.clear();
totalLoadedClasses.clear();
addUpperPlotData(classLoadingData);
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public void update(final Date from, final Date to) {
Date dataNewestDate = new Date(0);
if (!oldData.isEmpty()) {
dataNewestDate = oldData.get(oldData.size() - 1).getTimeStamp();
}
boolean leftAppend = from.before(oldFromDate);
// boolean rightAppend = to.after(dataNewestDate) &&
// (to.equals(newestDate) || to.after(newestDate));
boolean rightAppend = to.after(newestDate) || oldToDate.before(to);
List<ClassLoadingInformationData> adjustedClassLoadingData = Collections.emptyList();
if (oldData.isEmpty() || to.before(oldFromDate) || from.after(dataNewestDate)) {
// the old data is empty or the range does not fit, thus we need
// to access the whole range
List<ClassLoadingInformationData> data = (List<ClassLoadingInformationData>) dataAccessService.getDataObjectsFromToDate(template, from, to);
if (!data.isEmpty()) {
adjustedClassLoadingData = adjustSamplingRate(data, from, to, aggregator);
// we got some data, thus we can set the date
oldFromDate = (Date) from.clone();
oldToDate = (Date) to.clone();
if (newestDate.before(data.get(data.size() - 1).getTimeStamp())) {
newestDate = new Date(data.get(data.size() - 1).getTimeStamp().getTime());
}
}
oldData = data;
} else if (leftAppend && rightAppend) {
// we have some data in between, but we need to append something
// to the start and to the end
Date rightDate = new Date(newestDate.getTime() + 1);
Date leftDate = new Date(oldFromDate.getTime() - 1);
List<ClassLoadingInformationData> rightData = (List<ClassLoadingInformationData>) dataAccessService.getDataObjectsFromToDate(template, rightDate, to);
List<ClassLoadingInformationData> leftData = (List<ClassLoadingInformationData>) dataAccessService.getDataObjectsFromToDate(template, from, leftDate);
if (!leftData.isEmpty()) {
oldData.addAll(0, leftData);
oldFromDate = (Date) from.clone();
}
if (!rightData.isEmpty()) {
oldData.addAll(rightData);
oldToDate = (Date) to.clone();
if (newestDate.before(rightData.get(rightData.size() - 1).getTimeStamp())) {
newestDate = new Date(rightData.get(rightData.size() - 1).getTimeStamp().getTime());
}
}
adjustedClassLoadingData = adjustSamplingRate(oldData, from, to, aggregator);
} else if (rightAppend) {
// just append something on the right
Date rightDate = new Date(newestDate.getTime() + 1);
List<ClassLoadingInformationData> timerData = (List<ClassLoadingInformationData>) dataAccessService.getDataObjectsFromToDate(template, rightDate, to);
if (!timerData.isEmpty()) {
oldData.addAll(timerData);
oldToDate = (Date) to.clone();
if (newestDate.before(timerData.get(timerData.size() - 1).getTimeStamp())) {
newestDate = new Date(timerData.get(timerData.size() - 1).getTimeStamp().getTime());
}
}
adjustedClassLoadingData = adjustSamplingRate(oldData, from, to, aggregator);
} else if (leftAppend) {
// just append something on the left
Date leftDate = new Date(oldFromDate.getTime() - 1);
List<ClassLoadingInformationData> timerData = (List<ClassLoadingInformationData>) dataAccessService.getDataObjectsFromToDate(template, from, leftDate);
if (!timerData.isEmpty()) {
oldData.addAll(timerData);
oldFromDate = (Date) from.clone();
}
adjustedClassLoadingData = adjustSamplingRate(oldData, from, to, aggregator);
} else {
// No update is needed here because we already have all the
// needed data
adjustedClassLoadingData = adjustSamplingRate(oldData, from, to, aggregator);
}
final List<ClassLoadingInformationData> finalAdjustedClassLoadingData = adjustedClassLoadingData;
// updating the plots in the UI thread
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
setUpperPlotData(finalAdjustedClassLoadingData);
}
});
}
/**
* {@inheritDoc}
*/
@Override
public int getWeight(XYPlot subPlot) {
return weights.get(subPlot);
}
/**
* {@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 void dispose() {
}
}