package com.ibm.nmon.gui.chart.builder;
import java.util.List;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.statistics.HistogramDataset;
import org.jfree.data.statistics.HistogramType;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.TextAnchor;
import com.ibm.nmon.data.DataSet;
import com.ibm.nmon.data.DataRecord;
import com.ibm.nmon.data.DataType;
import com.ibm.nmon.data.DataTuple;
import com.ibm.nmon.data.definition.DataDefinition;
import com.ibm.nmon.gui.Styles;
import com.ibm.nmon.analysis.AnalysisRecord;
import com.ibm.nmon.analysis.Statistic;
import com.ibm.nmon.gui.chart.data.DataTupleHistogramDataset;
import com.ibm.nmon.chart.definition.HistogramChartDefinition;
public final class HistogramChartBuilder extends BaseChartBuilder<HistogramChartDefinition> {
// current vertical position for annotations
// incremented in addHistogram to prevent overlapping
private int insetTop = 5;
public HistogramChartBuilder() {
super();
}
@Override
protected JFreeChart createChart() {
HistogramDataset dataset = new DataTupleHistogramDataset();
NumberAxis dataAxis = new NumberAxis();
dataAxis.setAutoRangeIncludesZero(false);
NumberAxis valueAxis = new NumberAxis();
valueAxis.setAutoRangeIncludesZero(true);
XYBarRenderer renderer = new XYBarRenderer();
XYPlot plot = new XYPlot(dataset, dataAxis, valueAxis, renderer);
if (definition.hasSecondaryYAxis()) {
plot.setDataset(1, new DataTupleHistogramDataset());
valueAxis = new NumberAxis();
valueAxis.setAutoRangeIncludesZero(true);
plot.setRangeAxis(1, valueAxis);
plot.setRenderer(1, renderer);
plot.mapDatasetToRangeAxis(1, 1);
}
insetTop = 5;
return new JFreeChart("", null, plot, false);
}
protected void formatChart() {
super.formatChart();
chart.setTitle(definition.getTitle());
XYPlot plot = chart.getXYPlot();
if ("".equals(definition.getXAxisLabel())) {
plot.getDomainAxis().setLabel("Values");
}
else {
plot.getDomainAxis().setLabel(definition.getXAxisLabel());
}
if (definition.getXAxisRange() != null) {
plot.getDomainAxis().setRange(definition.getXAxisRange());
}
if ("".equals(definition.getYAxisLabel())) {
if (definition.usePercentYAxis()) {
plot.getRangeAxis().setLabel("Percent");
}
else {
plot.getRangeAxis().setLabel("Count");
}
}
else {
plot.getRangeAxis().setLabel(definition.getYAxisLabel());
}
HistogramDataset dataset = (HistogramDataset) plot.getDataset();
if (definition.usePercentYAxis()) {
NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
yAxis.setRange(0, 100);
dataset.setType(HistogramType.RELATIVE_FREQUENCY);
}
else {
dataset.setType(HistogramType.FREQUENCY);
}
if (definition.hasSecondaryYAxis()) {
// secondary axis always uses given label, even if blank
plot.getRangeAxis(1).setLabel(definition.getSecondaryYAxisLabel());
dataset = (HistogramDataset) plot.getDataset(1);
// secondary axis cannot use relative frequency
dataset.setType(HistogramType.FREQUENCY);
}
for (int i = 0; i < plot.getRendererCount(); i++) {
XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer(i);
renderer = ((XYBarRenderer) plot.getRenderer());
renderer.setMargin(0.15d);
renderer.setShadowVisible(false);
renderer.setDrawBarOutline(false);
renderer.setBarPainter(new GradientPainters.GradientXYBarPainter());
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{1} - {2}", Styles.NUMBER_FORMAT,
Styles.NUMBER_FORMAT));
renderer.setBaseOutlineStroke(OUTLINE_STROKE);
renderer.setBaseOutlinePaint(OUTLINE_COLOR);
}
for (int i = 0; i < plot.getRangeAxisCount(); i++) {
plot.getRangeAxis(i).setLabelFont(LABEL_FONT);
plot.getRangeAxis(i).setTickLabelFont(AXIS_FONT);
}
plot.getDomainAxis().setLabelFont(LABEL_FONT);
plot.getDomainAxis().setTickLabelFont(AXIS_FONT);
// gray grid lines
plot.setRangeGridlinePaint(GRID_COLOR);
plot.setRangeGridlineStroke(GRID_LINES);
}
public void addHistogram(AnalysisRecord analysis) {
if (chart == null) {
throw new IllegalStateException("initChart() must be called first");
}
if (definition == null) {
throw new IllegalArgumentException("HistogramChartDefintion cannot be null");
}
long start = System.nanoTime();
XYPlot plot = chart.getXYPlot();
DataTupleHistogramDataset dataset = (DataTupleHistogramDataset) plot
.getDataset(definition.hasSecondaryYAxis() ? 1 : 0);
DataSet data = analysis.getDataSet();
for (DataDefinition dataDefinition : definition.getData()) {
if (dataDefinition.matchesHost(data)) {
for (DataType type : dataDefinition.getMatchingTypes(data)) {
for (String field : dataDefinition.getMatchingFields(type)) {
String fieldName = definition.getHistogramNamingMode().getName(dataDefinition, data, type,
field, getGranularity());
List<Double> values = new java.util.ArrayList<Double>(data.getRecordCount());
// Use the analysis record's internal Interval is used rather than this
// class' record. Assume this class' interval and the record's are
// synchronized by the caller.
for (DataRecord record : data.getRecords(analysis.getInterval())) {
if (record.hasData(type)) {
values.add(record.getData(type)[type.getFieldIndex(field)]);
}
}
// convert to double for HistogramDataset
double[] toAdd = new double[values.size()];
for (int i = 0; i < toAdd.length; i++) {
toAdd[i] = values.get(i);
}
if (definition.getXAxisRange() == null) {
dataset.addSeries(fieldName, toAdd, definition.getBins());
}
else {
dataset.addSeries(fieldName, toAdd, definition.getBins(), definition.getXAxisRange()
.getLowerBound(), definition.getXAxisRange().getUpperBound());
}
dataset.associateTuple(fieldName, null, new DataTuple(data, type, field));
if (logger.isDebugEnabled()) {
logger.debug("{}: {}-{} added {} data points to chart '{}' in {}ms", data, type, field,
toAdd.length, definition.getTitle(), (System.nanoTime() - start) / 1000000.0d);
}
}
}
}
}
// add markers if necessary
if ((definition.getMarkerCount() > 0) && (dataset.getSeriesCount() > 0)) {
int seriesCount = dataset.getSeriesCount();
// output the field name in the marker if there is more than one series
boolean useFieldName = seriesCount > 1;
for (int i = 0; i < seriesCount; i++) {
DataTuple tuple = dataset.getTuple(i, 0);
for (Statistic stat : definition.getMarkers()) {
double value = stat.getValue(analysis, tuple.getDataType(), tuple.getField());
ValueMarker marker = new ValueMarker(value);
marker.setLabel((useFieldName ? dataset.getSeriesKey(i) + " " : "")
+ stat.getName(getGranularity()) + ": " + Styles.NUMBER_FORMAT.format(value));
marker.setStroke(Styles.ANNOTATION_STROKE);
marker.setPaint(Styles.ANNOTATION_COLOR);
marker.setLabelFont(Styles.ANNOTATION_FONT);
marker.setLabelPaint(Styles.ANNOTATION_COLOR);
marker.setLabelOffset(new RectangleInsets(insetTop, 5, 5, 10));
marker.setLabelTextAnchor(TextAnchor.TOP_RIGHT);
plot.addDomainMarker(marker);
insetTop += 20;
}
}
}
// chart.getXYPlot().configureRangeAxes();
if (chart.getLegend() == null) {
int seriesCount = chart.getXYPlot().getDataset(0).getSeriesCount();
if (definition.hasSecondaryYAxis()) {
seriesCount += chart.getXYPlot().getDataset(1).getSeriesCount();
}
if (seriesCount > 1) {
addLegend();
}
}
}
}