package org.jactr.eclipse.runtime.ui.probe.components;
/*
* default logging
*/
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javolution.util.FastList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.birt.chart.model.ChartWithAxes;
import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl;
import org.eclipse.birt.chart.model.component.Axis;
import org.eclipse.birt.chart.model.component.MarkerRange;
import org.eclipse.birt.chart.model.component.impl.MarkerRangeImpl;
import org.eclipse.birt.chart.model.data.NumberDataElement;
import org.eclipse.birt.chart.model.data.impl.NumberDataElementImpl;
import org.eclipse.swt.graphics.RGB;
import org.jactr.eclipse.runtime.marker.MarkerSessionDataStream;
import org.jactr.eclipse.runtime.session.stream.ILiveSessionDataStream;
import org.jactr.eclipse.runtime.session.stream.ILiveSessionDataStreamListener;
import org.jactr.eclipse.runtime.ui.marker.MarkerUI;
/**
* support class to enable rendering of markers in charts. When a marker is
* openend, it's end position is set to an arbitrary point in the future, beyond
* the runtime data window. When that position becomes visible, we extend it,
* until such time as the true marker end is reached.
*
* @author harrison
*/
public class MarkerSupport implements
AbstractBIRTProbeContainer.IChartUpdateListener
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(MarkerSupport.class);
private final MarkerSessionDataStream _msds;
private final AbstractBIRTProbeContainer _chartContainer;
private final ILiveSessionDataStreamListener<Long> _markerListener;
/*
* the last event we heard from
*/
private double _lastMarkerUpdateTime;
private final Set<Long> _added;
private final Set<Long> _modified;
private final Set<Long> _removed;
private final Map<Long, MarkerRange> _existingMarkers;
private final double _rangePadding;
public MarkerSupport(AbstractBIRTProbeContainer chartContainer,
MarkerSessionDataStream msds, double runtimeWindowSize)
{
_rangePadding = 2 * runtimeWindowSize;
_msds = msds;
_added = new TreeSet<Long>();
_modified = new TreeSet<Long>();
_removed = new TreeSet<Long>();
_existingMarkers = new TreeMap<Long, MarkerRange>();
_chartContainer = chartContainer;
_markerListener = new ILiveSessionDataStreamListener<Long>() {
public void dataChanged(ILiveSessionDataStream stream,
Collection<Long> added, Collection<Long> modified,
Collection<Long> removed)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String
.format("A:%s M:%s R:%s", added, modified, removed));
synchronized (MarkerSupport.this)
{
_added.addAll(added);
_modified.addAll(modified);
_removed.addAll(removed);
}
// request enventual update of the chart
_chartContainer.refresh();
}
};
// adding inline
_msds.addListener(_markerListener, null);
/*
* snag all the existing markers.. as new adds
*/
FastList<Long> added = FastList.newInstance();
_msds.getData(_msds.getStartTime(), _msds.getEndTime(), added);
synchronized (this)
{
_added.addAll(added);
}
FastList.recycle(added);
_chartContainer.addListener(this);
}
/**
* update the markers, this should be called on the SWT thread
*/
protected void updateMarkers()
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Updating markers %d, %d, %d", _added.size(),
_modified.size(), _removed.size()));
FastList<Long> markerContainer = FastList.newInstance();
try
{
ChartWithAxes chart = _chartContainer.getChart();
Axis xAxisPrimary = chart.getPrimaryBaseAxes()[0];
chart.getPrimaryOrthogonalAxis(xAxisPrimary);
// the additions
getAndClearContents(_added, markerContainer);
for (Long markerId : markerContainer)
{
MarkerRange range = _existingMarkers.get(markerId);
double openTime = _msds.getOpenTime(markerId);
if (Double.isNaN(openTime))
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Marker %d has no open time", markerId));
continue;
}
double closeTime = _msds.getCloseTime(markerId);
if (Double.isNaN(closeTime))
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Marker %d has no end time, estimating",
markerId));
closeTime = openTime + _rangePadding;
}
if (range == null)
{
String markerName = _msds.getName(markerId);
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Creating new Marker %d %s %.2f-%.2f",
markerId, markerName, openTime, closeTime));
String type = _msds.getType(markerId);
RGB newRGB = MarkerUI.getInstance().getRGB(type, true);
range = MarkerRangeImpl
.create(xAxisPrimary, NumberDataElementImpl.create(openTime),
NumberDataElementImpl.create(closeTime), ColorDefinitionImpl
.create(newRGB.red, newRGB.green, newRGB.blue));
range.getLabel().getCaption().setValue(markerName);
_existingMarkers.put(markerId, range);
}
}
getAndClearContents(_modified, markerContainer);
for (Long markerId : markerContainer)
{
MarkerRange range = _existingMarkers.get(markerId);
if (range == null)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String
.format("Updated marker %d is unknown", markerId));
continue;
}
double closeTime = _msds.getCloseTime(markerId);
if (Double.isNaN(closeTime))
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Still no end time for marker %d",
markerId));
continue;
}
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Setting endtime for marker %d to %.2f",
markerId, closeTime));
range.setEndValue(NumberDataElementImpl.create(closeTime));
}
getAndClearContents(_removed, markerContainer);
for (Long markerId : markerContainer)
{
MarkerRange range = _existingMarkers.remove(markerId);
if (range != null)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Removing marker %d", markerId));
xAxisPrimary.getMarkerRanges().remove(range);
}
}
/*
* now we check the marker range end times of the known markers. if any
* are still not closed, but their range times are less than now, we
* extend them
*/
double now = _msds.getEndTime();
for (Map.Entry<Long, MarkerRange> entry : _existingMarkers.entrySet())
{
MarkerRange range = entry.getValue();
NumberDataElement nde = (NumberDataElement) range.getEndValue();
if (nde.getValue() <= now)
{
long markerId = entry.getKey();
double closeTime = _msds.getCloseTime(markerId);
// push the end time back
if (Double.isNaN(closeTime))
{
closeTime = now + _rangePadding;
range.setEndValue(NumberDataElementImpl.create(closeTime));
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format(
"Extending marker %d's estimated endtime", markerId,
closeTime));
}
}
}
}
finally
{
FastList.recycle(markerContainer);
}
}
private void getAndClearContents(Set<Long> queue, FastList<Long> container)
{
container.clear();
synchronized (this)
{
container.addAll(queue);
queue.clear();
}
}
public void chartUpdated(boolean timeSpanHasChange, boolean scaleHasChanged,
boolean newData)
{
updateMarkers();
}
}