package com.ibm.nmon.gui.chart.builder;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import com.ibm.nmon.analysis.AnalysisRecord;
import com.ibm.nmon.analysis.Statistic;
import com.ibm.nmon.data.DataSet;
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.gui.chart.HighlightableBarChart;
import com.ibm.nmon.gui.chart.data.DataTupleCategoryDataset;
import com.ibm.nmon.chart.definition.BarChartDefinition;
public final class BarChartBuilder extends BaseChartBuilder<BarChartDefinition> {
public BarChartBuilder() {
super();
}
protected JFreeChart createChart() {
CategoryAxis categoryAxis = new CategoryAxis();
ValueAxis valueAxis = new NumberAxis();
BarRenderer renderer = null;
if (definition.isStacked()) {
renderer = new StackedBarRenderer();
}
else {
renderer = new BarRenderer();
}
CategoryPlot plot = new CategoryPlot(new DataTupleCategoryDataset(false), categoryAxis, valueAxis, renderer);
if (definition.hasSecondaryYAxis()) {
// second Y axis uses a separate dataset and axis
plot.setDataset(1, new DataTupleCategoryDataset(false));
valueAxis = new NumberAxis();
if (definition.isStacked()) {
plot.setRenderer(1, new StackedBarRenderer());
}
else {
plot.setRenderer(1, new BarRenderer());
}
plot.setRangeAxis(1, valueAxis);
plot.mapDatasetToRangeAxis(1, 1);
}
return new HighlightableBarChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, false);
}
@Override
protected void formatChart() {
super.formatChart();
chart.setTitle(definition.getTitle());
CategoryPlot plot = (CategoryPlot) chart.getPlot();
plot.getDomainAxis().setLabel(definition.getCategoryAxisLabel());
plot.getRangeAxis().setLabel(definition.getYAxisLabel());
if (definition.hasSecondaryYAxis()) {
plot.getRangeAxis(1).setLabel(definition.getSecondaryYAxisLabel());
}
if (definition.usePercentYAxis()) {
setPercentYAxis();
}
for (int i = 0; i < plot.getRendererCount(); i++) {
BarRenderer renderer = (BarRenderer) plot.getRenderer(i);
renderer.setShadowVisible(false);
renderer.setDrawBarOutline(false);
renderer.setBarPainter(new GradientPainters.GradientBarPainter());
renderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator("{1} {0} - {2} ({3})",
Styles.NUMBER_FORMAT));
renderer.setBaseOutlineStroke(OUTLINE_STROKE);
renderer.setBaseOutlinePaint(OUTLINE_COLOR);
plot.getRangeAxis(i).setLabelFont(LABEL_FONT);
plot.getRangeAxis(i).setTickLabelFont(AXIS_FONT);
}
plot.getDomainAxis().setCategoryMargin(0.15d);
plot.getDomainAxis().setTickMarksVisible(false);
// position of first bar start and last bar end
// 1.5% of the chart area within the axis will be blank space on each end
plot.getDomainAxis().setLowerMargin(.015);
plot.getDomainAxis().setUpperMargin(.015);
plot.getDomainAxis().setLabelFont(LABEL_FONT);
plot.getDomainAxis().setTickLabelFont(AXIS_FONT);
// gray grid lines
plot.setRangeGridlinePaint(GRID_COLOR);
plot.setRangeGridlineStroke(GRID_LINES);
}
public void addBar(AnalysisRecord record) {
if (chart == null) {
throw new IllegalStateException("initChart() must be called first");
}
if (definition == null) {
throw new IllegalArgumentException("BarChartDefintion cannot be null");
}
// Note that both this builder and the AnalysisRecord have an Interval stored. Note that
// these are _not_ synchronized here under the assumption that a) the client application has
// already done this and b) the client application is caching a number of records and does
// not expect different records to have different Intervals. So, the record's internal
// Interval is used rather than this class' record.
CategoryPlot plot = (CategoryPlot) chart.getPlot();
DataTupleCategoryDataset dataset = (DataTupleCategoryDataset) plot
.getDataset(definition.hasSecondaryYAxis() ? 1 : 0);
DataSet data = record.getDataSet();
Statistic previousStat = null;
for (DataDefinition dataDefinition : definition.getData()) {
if (dataDefinition.matchesHost(data)) {
for (DataType type : dataDefinition.getMatchingTypes(data)) {
for (String field : dataDefinition.getMatchingFields(type)) {
String barName = definition.getBarNamingMode().getName(dataDefinition, data, type, field,
getGranularity());
String categoryName = definition.getCategoryNamingMode().getName(dataDefinition, data, type,
field, getGranularity());
Statistic currentStat = dataDefinition.getStatistic();
double value = currentStat.getValue(record, type, field);
if ((previousStat != null) && (previousStat != currentStat)) {
dataset.setCategoriesHaveDifferentStats(true);
}
previousStat = currentStat;
dataset.addValue(value, barName, categoryName);
dataset.associateTuple(barName, categoryName, new DataTuple(data, type, field));
}
}
}
}
// subtract the value for each category from the previous values
if (definition.isSubtractionNeeded() && (dataset.getRowCount() != 0)) {
for (int i = 0; i < dataset.getColumnCount(); i++) {
// prime total with the first value in each bar
// the first value is not modified
double total = (double) dataset.getValue(0, i).doubleValue();
String barName = (String) dataset.getColumnKey(i);
for (int j = 1; j < dataset.getRowCount(); j++) {
double value = dataset.getValue(j, i).doubleValue() - total;
String categoryName = (String) dataset.getRowKey(j);
dataset.setValue(value, categoryName, barName);
total += value;
}
}
}
if (chart.getLegend() == null) {
int rowCount = plot.getDataset(0).getRowCount();
if (definition.hasSecondaryYAxis()) {
rowCount += plot.getDataset(1).getRowCount();
}
if (rowCount > 1) {
addLegend();
}
}
// smaller font size for charts with a lot of bars
if (dataset.getColumnCount() > 32) {
plot.getDomainAxis().setTickLabelFont(AXIS_FONT.deriveFont(AXIS_FONT.getSize() * 0.9f));
}
plot.configureRangeAxes();
}
public void setPercentYAxis() {
NumberAxis yAxis = (NumberAxis) ((CategoryPlot) chart.getPlot()).getRangeAxis();
yAxis.setRange(0, 100);
}
}