package rocks.inspectit.ui.rcp.editor.graph; import java.awt.Color; import java.text.DateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.forms.widgets.FormToolkit; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.DateTickUnit; import org.jfree.chart.axis.DateTickUnitType; import org.jfree.chart.axis.TickUnits; import org.jfree.chart.plot.CombinedDomainXYPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.data.Range; import org.jfree.experimental.chart.swt.ChartComposite; import rocks.inspectit.shared.all.util.TimeFrame; import rocks.inspectit.shared.cs.storage.label.AbstractStorageLabel; import rocks.inspectit.shared.cs.storage.label.type.impl.DataTimeFrameLabelType; import rocks.inspectit.ui.rcp.editor.AbstractSubView; import rocks.inspectit.ui.rcp.editor.ISubView; import rocks.inspectit.ui.rcp.editor.graph.plot.DateAxisZoomNotify; import rocks.inspectit.ui.rcp.editor.graph.plot.PlotController; import rocks.inspectit.ui.rcp.editor.graph.plot.ZoomListener; import rocks.inspectit.ui.rcp.editor.preferences.IPreferenceGroup; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceEventCallback.PreferenceEvent; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId.LiveMode; import rocks.inspectit.ui.rcp.model.SensorTypeEnum; import rocks.inspectit.ui.rcp.repository.RepositoryDefinition; import rocks.inspectit.ui.rcp.repository.StorageRepositoryDefinition; /** * This sub-view can create charts which can contain themselves some plots. The plots are defined by * {@link PlotController}. * * @author Patrice Bouillet * */ public class GraphSubView extends AbstractSubView { /** * The composite used to draw the items to. */ private Composite composite; /** * The {@link JFreeChart} chart. */ private JFreeChart chart; /** * The plot controller defines the visualized plots in the chart. */ private PlotController plotController; /** * If we are in the auto update mode. */ private boolean autoUpdate = LiveMode.ACTIVE_DEFAULT; /** * One minute in milliseconds. */ private static final long ONE_MINUTE = 60000L; /** * Ten minutes in milliseconds. */ private static final long TEN_MINUTES = ONE_MINUTE * 10; /** * The zoom listener. */ private ZoomListener zoomListener; /** * Defines if a refresh job is currently already executing. */ private volatile boolean jobInSchedule = false; /** * The constructor taking one parameter and creating a {@link PlotController}. * * @param fqn * The fully-qualified-name of the corresponding sensor type. */ public GraphSubView(String fqn) { this.plotController = PlotFactory.createDefaultPlotController(fqn); } /** * The constructor taking one parameter and creating a {@link PlotController}. * * @param sensorTypeEnum * The sensor type enumeration of the corresponding sensor type. */ public GraphSubView(SensorTypeEnum sensorTypeEnum) { this.plotController = PlotFactory.createDefaultPlotController(sensorTypeEnum); } /** * {@inheritDoc} */ @Override public void init() { plotController.setInputDefinition(getRootEditor().getInputDefinition()); } /** * {@inheritDoc} */ @Override public void createPartControl(Composite parent, FormToolkit toolkit) { // set the input definition plotController.setRootEditor(getRootEditor()); // create the composite composite = toolkit.createComposite(parent); composite.setLayout(new FillLayout()); // create the chart chart = createChart(); if (!plotController.showLegend()) { chart.removeLegend(); } Color color = new Color(toolkit.getColors().getBackground().getRed(), toolkit.getColors().getBackground().getGreen(), toolkit.getColors().getBackground().getBlue()); chart.setBackgroundPaint(color); new ChartComposite(composite, SWT.NONE, chart, ChartComposite.DEFAULT_WIDTH, ChartComposite.DEFAULT_HEIGHT, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, true, true, true, true, true, true) { /** * {@inheritDoc} * <p> * On print we want to set the title so it's visible in the print page. */ @Override public boolean print(GC gc) { chart.setTitle(getRootEditor().getTitle()); boolean res = super.print(gc); chart.setTitle(""); return res; } }; } /** * Creates and returns a {@link JFreeChart} chart. * * @return The {@link JFreeChart} chart. */ private JFreeChart createChart() { DateAxisZoomNotify domainAxis = new DateAxisZoomNotify(); domainAxis.setLowerMargin(0.0d); domainAxis.setAutoRangeMinimumSize(100000.0d); long now = System.currentTimeMillis(); domainAxis.setRange(new Range(now - TEN_MINUTES, now + ONE_MINUTE), true, false); // set the ticks to display in the date axis TickUnits source = new TickUnits(); source.add(new DateTickUnit(DateTickUnitType.MINUTE, 1, DateFormat.getTimeInstance(DateFormat.SHORT))); source.add(new DateTickUnit(DateTickUnitType.MINUTE, 5, DateFormat.getTimeInstance(DateFormat.SHORT))); source.add(new DateTickUnit(DateTickUnitType.MINUTE, 30, DateFormat.getTimeInstance(DateFormat.SHORT))); source.add(new DateTickUnit(DateTickUnitType.HOUR, 1, DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT))); source.add(new DateTickUnit(DateTickUnitType.HOUR, 3, DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT))); source.add(new DateTickUnit(DateTickUnitType.DAY, 1, DateFormat.getDateInstance(DateFormat.SHORT))); source.add(new DateTickUnit(DateTickUnitType.DAY, 7, DateFormat.getDateInstance(DateFormat.MEDIUM))); source.add(new DateTickUnit(DateTickUnitType.MONTH, 1, DateFormat.getDateInstance(DateFormat.MEDIUM))); source.add(new DateTickUnit(DateTickUnitType.YEAR, 1, DateFormat.getDateInstance(DateFormat.MEDIUM))); source.add(new DateTickUnit(DateTickUnitType.YEAR, 10, DateFormat.getDateInstance(DateFormat.MEDIUM))); domainAxis.setStandardTickUnits(source); domainAxis.setAutoTickUnitSelection(true); addZoomListener(domainAxis); CombinedDomainXYPlot plot = new CombinedDomainXYPlot(domainAxis); plot.setGap(10.0); // add the subplots... List<XYPlot> subPlots = plotController.getPlots(); for (XYPlot subPlot : subPlots) { plot.add(subPlot, plotController.getWeight(subPlot)); } plot.setOrientation(PlotOrientation.VERTICAL); TimeFrame timeFrame = getInitialDataTimeFrame(); if (null != timeFrame) { // set min/max dates ((DateAxis) plot.getDomainAxis()).setMinimumDate(timeFrame.getFrom()); ((DateAxis) plot.getDomainAxis()).setMaximumDate(timeFrame.getTo()); // inform the time-line widget via event PreferenceEvent preferenceEvent = new PreferenceEvent(PreferenceId.TIMELINE); Map<IPreferenceGroup, Object> map = new HashMap<>(); map.put(PreferenceId.TimeLine.FROM_DATE_ID, timeFrame.getFrom()); map.put(PreferenceId.TimeLine.TO_DATE_ID, timeFrame.getTo()); preferenceEvent.setPreferenceMap(map); getRootEditor().getPreferencePanel().fireEvent(preferenceEvent); } // return a new chart containing the overlaid plot... return new JFreeChart(plot); } /** * Adds the zoom listener to the domain axis. * * @param domainAxis * The domain axis. */ private void addZoomListener(DateAxisZoomNotify domainAxis) { if (null == zoomListener) { zoomListener = new ZoomListener() { @Override public void zoomOccured() { if (autoUpdate) { autoUpdate = false; getRootEditor().getPreferencePanel().disableLiveMode(); } doRefresh(); } }; } domainAxis.addZoomListener(zoomListener); } /** * Returns initial data time frame if one is defined. * * @return Returns initial data time frame if one is defined. */ private TimeFrame getInitialDataTimeFrame() { RepositoryDefinition repositoryDefinition = getRootEditor().getInputDefinition().getRepositoryDefinition(); if (repositoryDefinition instanceof StorageRepositoryDefinition) { StorageRepositoryDefinition storageRepositoryDefinition = (StorageRepositoryDefinition) repositoryDefinition; List<AbstractStorageLabel<TimeFrame>> labels = storageRepositoryDefinition.getLocalStorageData().getLabels(new DataTimeFrameLabelType()); if (CollectionUtils.isNotEmpty(labels)) { return labels.get(0).getValue(); } } return null; } /** * {@inheritDoc} */ @Override public Control getControl() { return composite; } /** * {@inheritDoc} */ @Override public ISelectionProvider getSelectionProvider() { return null; } /** * {@inheritDoc} */ @Override public void setDataInput(List<? extends Object> data) { // nothing to do here } /** * {@inheritDoc} */ @Override public Set<PreferenceId> getPreferenceIds() { return plotController.getPreferenceIds(); } /** * {@inheritDoc} */ @Override public void preferenceEventFired(PreferenceEvent preferenceEvent) { if (PreferenceId.TIMELINE.equals(preferenceEvent.getPreferenceId())) { XYPlot plot = (XYPlot) chart.getPlot(); DateAxis axis = (DateAxis) plot.getDomainAxis(); Map<IPreferenceGroup, Object> preferenceMap = preferenceEvent.getPreferenceMap(); if (preferenceMap.containsKey(PreferenceId.TimeLine.TO_DATE_ID)) { Date toDate = (Date) preferenceMap.get(PreferenceId.TimeLine.TO_DATE_ID); axis.setMaximumDate(toDate); } if (preferenceMap.containsKey(PreferenceId.TimeLine.FROM_DATE_ID)) { Date fromDate = (Date) preferenceMap.get(PreferenceId.TimeLine.FROM_DATE_ID); axis.setMinimumDate(fromDate); } doRefresh(); } if (PreferenceId.LIVEMODE.equals(preferenceEvent.getPreferenceId())) { Map<IPreferenceGroup, Object> preferenceMap = preferenceEvent.getPreferenceMap(); if (preferenceMap.containsKey(PreferenceId.LiveMode.BUTTON_LIVE_ID)) { autoUpdate = (Boolean) preferenceMap.get(PreferenceId.LiveMode.BUTTON_LIVE_ID); if (autoUpdate) { doRefresh(); } } } plotController.preferenceEventFired(preferenceEvent); } /** * {@inheritDoc} */ @Override public void doRefresh() { if (checkDisposed()) { return; } if (!jobInSchedule) { jobInSchedule = true; XYPlot plot = (XYPlot) chart.getPlot(); DateAxis axis = (DateAxis) plot.getDomainAxis(); final Date minDate = axis.getMinimumDate(); final Date maxDate = autoUpdate ? new Date(System.currentTimeMillis()) : axis.getMaximumDate(); if (autoUpdate) { axis.setMaximumDate(maxDate); } Job job = new Job(getDataLoadingJobName()) { @Override protected IStatus run(IProgressMonitor monitor) { try { plotController.update(minDate, maxDate); return Status.OK_STATUS; } catch (Throwable throwable) { // NOPMD throw new RuntimeException("Unknown exception occurred trying to refresh the view.", throwable); } finally { jobInSchedule = false; } } }; job.schedule(); } } /** * {@inheritDoc} */ @Override public ISubView getSubViewWithInputController(Class<?> inputControllerClass) { if (Objects.equals(inputControllerClass, plotController.getClass())) { return this; } return null; } /** * Returns true if the composite holding the chart in the sub-view is disposed. False otherwise. * * @return Returns true if the composite holding the chart in the sub-view is disposed. False * otherwise. */ private boolean checkDisposed() { return composite.isDisposed(); } /** * {@inheritDoc} */ @Override public void dispose() { plotController.dispose(); } }