/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2008], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.image.chart;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import org.hyperic.image.WebImage;
import org.hyperic.util.data.IDisplayDataPoint;
import org.hyperic.util.data.IHighLowDataPoint;
import org.hyperic.util.data.IStackedDataPoint;
import org.hyperic.util.units.FormattedNumber;
import org.hyperic.util.units.UnitsConstants;
import org.hyperic.util.units.UnitsFormat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Chart is an abstract base class for the Covalent charting library. Chart uses
* Java AWT drawing to draw charts dynamically into a Java Image which can be
* then be used to display the chart in an application. The most common use
* of the charting library is to draw the chart image and then convert the
* image to a GIF to send to a Web Browser.
*
* Chart is responsible for all of the background drawing of the Chart
* the Chart frame, text labels, data point crossing lines, etc. Sublcasses
* of Chart draw on top of the image produce by the Chart class to draw the
* actual lines, columns, etc. that represent the data points of the chart.
*
* To create a chart you instantiate a subclass of Chart (e.g., LineChart,
* ColumnChart, etc.), call getData() to retrieve a collection, poplulate
* the collection and then call Chart.getImage() to get a java.awt.Image
* object that represents the drawn chart.
*
* The chart class is very configurable through get/set methods including
* configuring things like the text for labels, fonts, line colors, etc.
* After changing one or more options you call Chart.getImage() again to
* get a new image based on your options.
*
* The chart should have at least three datums in its Datum collection to
* display without errors.
*
* @see java.awt.Image
*/
public abstract class Chart extends WebImage
{
private Log log = LogFactory.getLog( Chart.class.getName() );
////////////////////////////////////////////////
// Static Variables
public static final String EMPTY_STRING = "";
protected static final String ARG_MUST_BE_ZERO_OR_GREATER =
"Argument value must be zero or greater";
protected static final String AVG = "Average";
protected static final String BASELINE = "Baseline";
protected static final String LOW = "Low";
protected static final String PEAK = "Peak";
protected static final String DEFAULT_UNIT_LEGEND = "TIME";
protected static final String DEFAULT_VALUE_LEGEND = "VALUE";
private static final String NO_DATA = "No Metric Data Available";
private static final int DEFAULT_LINE_WIDTH = 1;
private static final int DEFAULT_VALUE_INDENT = 0;
private static final int DEFAULT_VALUE_LINES = 11;
private static final int DEFAULT_TEXT_WHITESPACE = 3;
private static final int DEFAULT_TICK_MARK_HEIGHT = 6;
protected static final int VARIABLE_HEIGHT = Integer.MIN_VALUE;
protected static final int VARIABLE_WIDTH = Integer.MIN_VALUE;
private static final Color DEFAULT_FRAME_COLOR =
new Color(0xE6, 0xE5, 0xE5); //new Color(0xCC, 0xCC, 0xCC);
private static final Color DEFAULT_LEGEND_TEXT_COLOR =
Color.BLACK;
private static final Color DEFAULT_X_LINE_COLOR =
new Color(0xE6, 0xE5, 0xE5);
private static final Color DEFAULT_TEXT_COLOR =
new Color(0x80, 0x80, 0x80);
private static final Color DEFAULT_AVERAGE_COLOR =
new Color(0x8C, 0xBF, 0x73);
private static final Color DEFAULT_BASELINE_COLOR =
new Color(0xBF, 0xB2, 0x73);
private static final Color DEFAULT_LOW_COLOR =
new Color(0x73, 0xBF, 0xB3);
private static final Color DEFAULT_PEAK_COLOR =
new Color(0xD9, 0x98, 0x98);
private static final Color DEFAULT_HIGH_RANGE_COLOR =
new Color(0xF7, 0xEB, 0xEB);
private static final Color DEFAULT_LOW_RANGE_COLOR =
new Color(0xEB, 0xF7, 0xF6);
protected static final Font DEFAULT_LABEL_FONT =
new Font("Helvetica", Font.PLAIN, 10);
protected static final Font DEFAULT_LEGEND_PLAIN =
new Font("Helvetica", Font.PLAIN, 11);
protected static final Font DEFAULT_LEGEND_FONT =
new Font("Helvetica", Font.BOLD, 11);
protected FontMetrics m_metricsLabel;
protected FontMetrics m_metricsLegend;
////////////////////////////////////////////////
// Instance Variables
private String m_strUnitLegend = DEFAULT_UNIT_LEGEND;
private String m_strValueLegend = DEFAULT_VALUE_LEGEND;
private boolean m_useAbsTimeLabels = false;
private SmartLabelMaker m_smartLabelMaker = null;
private Color m_clrFrame = DEFAULT_FRAME_COLOR;
private Color m_clrLegendText = DEFAULT_LEGEND_TEXT_COLOR;
protected int m_fmtType;
protected int m_fmtScale;
protected double m_dAvgValue = 0;
protected double m_dLowValue = Double.POSITIVE_INFINITY;
protected double m_dPeakValue = Double.NEGATIVE_INFINITY;
protected double[] m_adRangeMarks;
protected double m_floor;
protected boolean m_bNoData;
private String m_strTitle = EMPTY_STRING;
private ArrayList m_collDataPointColl = new ArrayList(1);
private ArrayList m_collEvtPointColl = new ArrayList(1);
private String m_strNoData = NO_DATA;
public int yTopLegend;
public int yBottomLegend;
public int xVertLegend;
public int yHorzLabels;
public int x2VertLabels;
public int xRLabel;
public int xVertMarks;
public int x2VertMarks;
public int xLabelsSkip = 4; // Default to label every 4 ticks
////////////////////////////////////////////////
// Property Variables
/**
* Color of the average data point horizontal line.
*/
public Color averageLineColor = DEFAULT_AVERAGE_COLOR;
/**
* Color of the baseline horizontal line.
*/
public Color baselineColor = DEFAULT_BASELINE_COLOR;
/**
* Top number for the left axis of the chart.
*/
public double ceiling = 0;
/**
* Retrieves the chart's interior background color.
*/
public Color chartColor = Color.WHITE;
/**
* Bottom number for the left axis of the chart.
*/
public double floor = 0;
/**
* Baseline in the chart.
*/
public double baseline = 0;
/**
* High range in the chart.
*/
public double highRange = Double.NaN;
/**
* High range color in the chart.
*/
public Color highRangeColor = DEFAULT_HIGH_RANGE_COLOR;
/**
* Font that is used to draw the legend for the chart's X and Y axis.
*/
public Font legendFont = DEFAULT_LEGEND_FONT;
/**
* Color that is used to draw the legend text for the chart's X and Y axis.
*/
public Color legendTextColor = DEFAULT_LEGEND_TEXT_COLOR;
/**
* Width of lines in the chart.
*/
public int lineWidth = DEFAULT_LINE_WIDTH;
/**
* Retrieves the color of the low data point horizontal line.
*/
public Color lowLineColor = DEFAULT_LOW_COLOR;
/**
* Low range in the chart.
*/
public double lowRange = Double.NaN;
/**
* High range color in the chart.
*/
public Color lowRangeColor = DEFAULT_LOW_RANGE_COLOR;
/**
* Color of the peak data point horizontal line.
*/
public Color peakLineColor = DEFAULT_PEAK_COLOR;
/**
* Sets a fixed size for the label width on the right vertical axis.
*/
public int rightLabelWidth = -1;
/**
* Calculates and shows an Average line in the chart.
*/
public boolean showAverage = false;
/**
* Shows a Baseline in the chart.
*/
public boolean showBaseline = false;
/**
* Shows labels on the X axis bottom side.
*/
public boolean showBottomLabels = true;
/**
* Shows a top legend.
*/
public boolean showBottomLegend = true;
/**
* Shows events plotted on a chart.
*/
public boolean showEvents = true;
/**
* Show full labels on the bottom x axis.
*/
public boolean showFullLabels = false;
/**
* Shows a shaded high range in the chart.
*/
public boolean showHighRange = false;
/**
* Shows labels on the Y axis left side.
*/
public boolean showLeftLabels = true;
/**
* Shows a Left legend.
*/
public boolean showLeftLegend = true;
/**
* Calculates and shows a Low line in the chart.
*/
public boolean showLow = false;
/**
* Shows a shaded low range in the chart.
*/
public boolean showLowRange = false;
/**
* Calculates and shows a peak line in the chart.
*/
public boolean showPeak = false;
/**
* Shows labels on the Y axis right side.
*/
public boolean showRightLabels = true;
/**
* Shows a Right legend.
*/
public boolean showRightLegend = false;
/**
* Shows labels on the X axis top side.
*/
public boolean showTopLabels = false;
/**
* Shows a top legend.
*/
public boolean showTopLegend = false;
/**
* Shows vertical crossing lines at each data point on the y axis.
*/
public boolean showUnitLines = false;
/**
* Shows horizontal crossing lines at each data point on the x axis.
*/
public boolean showValueLines = false;
/**
* Shows values.
*/
public boolean showValues = true;
/**
* The width of the white space between tick marks and the label text.
*/
public int textWhitespace = DEFAULT_TEXT_WHITESPACE;
/**
* The height, in pixels, of the tick mark lines that extend
* outside the chart's border. The tick marks for data points that don't
* have a label are drawn at half this height.
*/
public int tickMarkHeight = DEFAULT_TICK_MARK_HEIGHT;
/**
* Number of pixels to indent the value axis.
*/
public int valueIndent = DEFAULT_VALUE_INDENT;
/**
* Number of value lines that the chart draws at data points along
* the chart's value axis. For vertical chart's this is the Y axis. For
* horizontal charts this is the chart's X axis. This number includes the
* chart's top and bottom border.
*/
public int valueLines = DEFAULT_VALUE_LINES;
/**
* Color of the border lines, horizontal lines and tick marks
*/
public Color xLineColor = DEFAULT_X_LINE_COLOR;
/**
* Amount to offset the chart on the X axis in pixels.
*/
public int xOffset = 0;
/**
* Amount to offset the chart on the Y axis in pixels.
*/
public int yOffset = 0;
////////////////////////////////////////////////
// Constructors
/**
* Constructs a Chart class with a default width of 755 pixels and a
* default height of 300 pixels.
*/
protected Chart() {
this(1);
}
/**
* Constructs a Chart class with a default width of 755 pixels and a
* default height of 300 pixels.
*
* @param charts
* The number of charts to display.
*/
protected Chart(int charts) {
this(Chart.DEFAULT_WIDTH, Chart.DEFAULT_HEIGHT, charts);
}
/**
* Constructs a Chart class with a specified width and height.
*
* @param width
* The width of the chart in pixels.
* @param height
* The height of the chart in pixels.
*/
protected Chart(int width, int height) {
this(width, height, 1);
}
/**
* Constructs a Chart class with a specified width and height.
*
* @param width
* The width of the chart in pixels.
* @param height
* The height of the chart in pixels.
* @param charts
* The number of charts to display.
*/
protected Chart(int width, int height, int charts) {
super(width, height);
this.shadowWidth = 0;
this.textColor = DEFAULT_TEXT_COLOR;
if(charts <= 0) charts = 1;
this.setNumberDataSets(charts);
this.initFonts();
}
////////////////////////////////////////////////
// Methods
protected void draw(Graphics2D g) {
super.draw(g);
this.draw( new ChartGraphics(this, g) );
}
protected Rectangle draw(ChartGraphics g) {
/////////////////////////////////////////////////////////////
// Draw the chart outline and fills the interior
// Fill chart background
Rectangle rect = this.getInteriorRectangle(g);
if(this.m_bNoData == false) {
// Fill chart interior
g.graphics.setColor(this.m_clrFrame);
g.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
g.graphics.setColor(this.chartColor);
g.graphics.fillRect(rect.x + this.lineWidth,
rect.y + this.lineWidth,
rect.width - (this.lineWidth),
rect.height - (this.lineWidth) );
} else {
FontMetrics metrics = g.graphics.getFontMetrics(this.legendFont);
g.graphics.setColor(this.m_clrLegendText);
g.graphics.setFont(DEFAULT_LEGEND_PLAIN);
g.graphics.drawString( this.m_strNoData,
(this.width / 2) - (metrics.stringWidth(this.m_strNoData) / 2),
this.yOffset + (this.height / 2) + (metrics.getAscent() / 2) );
}
return rect;
}
protected void calc(Graphics2D g) {
FormattedNumber[] fmtValueLabels = UnitsFormat.formatSame(m_adRangeMarks, m_fmtType, m_fmtScale);
Rectangle rect = new Rectangle(this.xOffset, this.yOffset, this.width, this.height);
// Calculate the X axis
rect.x += this.leftBorder;
if(this.showHighRange == true || this.showLowRange == true) rect.x += this.lineWidth;
int xVertLabels = (this.showLeftLegend == true) ? rect.x + m_metricsLegend.charWidth('V') + (this.textWhitespace * 2) : 0;
int cxLabels = (this.rightLabelWidth < 0) ? this.getYLabelWidth(g) : this.rightLabelWidth;
if(this.showLeftLabels == true) {
x2VertLabels = xVertLabels + cxLabels;
xVertMarks = x2VertLabels + this.textWhitespace;
rect.x = xVertMarks + this.tickMarkHeight;
}
rect.width = rect.width - rect.x - this.rightBorder;
if(this.showRightLabels == true) {
rect.width -= this.tickMarkHeight;
rect.width = rect.width - cxLabels;
} else {
// We need to bring the right frame in a little if there's no right
// label, but there are top or bottom labels.
String[] labels = this.getXLabels();
if(labels != null && labels.length > 1)
rect.width -= (this.m_metricsLabel.stringWidth(labels[labels.length - 1]) / 2);
}
// Adjust the interior of the rectangle
Rectangle rectAdj = this.adjustRectangle(g, rect);
x2VertMarks = rect.x + rect.width + (this.lineWidth * 2);
if(this.showRightLabels == true) x2VertMarks += this.tickMarkHeight;
xRLabel = x2VertMarks + this.textWhitespace;
int yVertLegend = rect.y + (this.height / 2) - (m_metricsLegend.getAscent() * this.m_strValueLegend.length() / 2);
// Calculate the Y axis
rect.y = rect.y + this.topBorder;
if(this.showTopLegend == true) {
rect.y += m_metricsLegend.getAscent();
yTopLegend = rect.y;
rect.y += this.textWhitespace;
}
if(this.showTopLabels == true)
rect.y += (this.getXLabelHeight() + this.tickMarkHeight);
if(this.showTopLegend == false && this.showTopLabels == false)
rect.y += (m_metricsLabel.getAscent() / 2);
yBottomLegend = this.yOffset + (rect.height - this.bottomBorder);
int yHorzMarks;
if(this.showBottomLegend == false && this.showBottomLabels == false) {
yHorzLabels = yBottomLegend - (m_metricsLabel.getAscent() / 2);
yHorzMarks = yHorzLabels;
} else {
yHorzLabels = yBottomLegend - ((this.showBottomLegend == true) ? this.getXLabelHeight() : 0);
yHorzMarks = yHorzLabels - ((this.showBottomLabels == true) ? (this.m_metricsLabel.getAscent() + this.tickMarkHeight) : 0);
}
int y2Rect = yHorzMarks - this.lineWidth;
rect.height = y2Rect - rect.y;
int xHorzLegend = (this.width / 2) - (m_metricsLegend.stringWidth(this.getUnitLegend()) / 2);
int xHorzMarks = rect.x + this.valueIndent;
}
/**
* Give the child class an opportunity to change the size of the interior
* rectangle. This is done to make the tick marks fit symetrically in the
* chart rectangle.
*/
protected Rectangle adjustRectangle(Graphics2D g, Rectangle rect) {
return rect;
}
/**
* Calculates the high, low and average values of the chart data set.
*/
protected void calcRanges() {
int cActualVals = 0;
int index;
double unit;
double topRange;
// Calculate Top and Bottom Range
double dVal;
// ///////////////////////////////////////////////////////////////
// Iterator through the DataSets to calculate the avg, low & peak
Iterator iterDataSet = this.m_collDataPointColl.iterator();
while(iterDataSet.hasNext() == true) {
// Each DataSet has a collection of data points.
Iterator iterDataPt =
((DataPointCollection)iterDataSet.next()).iterator();
while(iterDataPt.hasNext() == true) {
IDisplayDataPoint datapt = (IDisplayDataPoint)iterDataPt.next();
// Skip NaN
if(Double.isNaN(datapt.getValue()) == true)
continue;
if (checkHighLow()) {
if (datapt instanceof IHighLowDataPoint) {
IHighLowDataPoint hlPt = (IHighLowDataPoint) datapt;
if (hlPt.getLowValue() != Double.NaN)
this.m_dLowValue = Math.min(
this.m_dLowValue, hlPt.getLowValue());
if (hlPt.getHighValue() != Double.NaN)
this.m_dPeakValue = Math.max(
this.m_dPeakValue, hlPt.getHighValue());
}
}
double[] vals = datapt instanceof IStackedDataPoint ?
((IStackedDataPoint)datapt).getValues() :
new double[] { datapt.getValue() };
for(int i = 0;i < vals.length;i ++) {
dVal = vals[i];
// Accumulate a total and number of points that make up the
// total
this.m_dAvgValue += dVal;
cActualVals ++;
// Set the low
this.m_dLowValue = Math.min(this.m_dLowValue, dVal);
// Set the high
this.m_dPeakValue = Math.max(this.m_dPeakValue, dVal);
}
}
}
// Set the NoData flag and exit if we don't have any
this.m_bNoData = (cActualVals == 0);
if(this.m_bNoData == true) {
this.m_adRangeMarks = new double[0];
return;
}
// Caclulate the average
this.m_dAvgValue /= cActualVals;
///////////////////////////////////////////////////////////////////
// Calculate the value axis units (X for horiz, Y for vertical)
if(this.ceiling == this.floor) {
double range = (this.m_dPeakValue - this.m_dLowValue);
if(range != 0) {
// Buffer the top and bottom by 10%
double buffer = range * .1;
double topbuf = buffer / 2;
double botbuf = (this.m_dLowValue - topbuf < 0)
? m_dLowValue : topbuf;
range += (topbuf + botbuf);
unit = range / (this.valueLines - 1);
double tmp = range / unit;
this.m_floor = this.m_dLowValue - botbuf;
topRange = this.m_dPeakValue + topbuf;
} else {
// If the peak value and low value are the same create a range
// that puts the charted value 1/2 up the chart
this.m_floor = 0;
if(this.m_dPeakValue == 0) {
topRange = (this.valueLines - 1);
if(this.m_fmtType == UnitsConstants.UNIT_DURATION) {
topRange *= 1000;
}
} else {
topRange = this.m_dPeakValue * 2;
}
unit = (topRange - this.m_floor) / (this.valueLines - 1);
}
} else {
// We just accept the floor and ceiling if they are preset by
// whoever instantiated the chart
this.m_floor = this.floor;
unit = (this.ceiling - this.floor) / (this.valueLines - 1);
}
////////////////////////////////////////////////////////////////////
// Calculate Cross Line Values
this.m_adRangeMarks = new double[this.valueLines];
for(int i = 0;i < this.valueLines;i++)
this.m_adRangeMarks[i] = this.m_floor + (i * unit);
}
protected int calcVariableHeight() {
return this.height;
}
protected int calcVariableWidth() {
return this.width;
}
/**
* Calculates the label width of the horizontal axis of the chart.
*
* @param graph
* The java.awt.Graphics context to draw into.
*
* @return
* The width of the widest label on the X (horizontal) axis.
*/
protected int getXLabelWidth()
{
int iWidth;
int iMaxWidth = 0;
Iterator iter = this.getDataPoints().iterator();
while(iter.hasNext())
{
iWidth = m_metricsLabel.stringWidth(
((IDisplayDataPoint)iter.next()).getLabel());
if(iWidth > iMaxWidth)
iMaxWidth = iWidth;
}
return iMaxWidth;
}
protected int getXLegendHeight() {
int height = m_metricsLegend.getAscent() + (this.textWhitespace * 2);
int result = 0;
if(this.showTopLegend == true)
result += height;
if(this.showBottomLegend == true)
result += height;
return result;
}
/**
* Calculates the label width of the vertical axis of the chart.
*
* @param graph
* The ChartGraphics context to draw into.
*
* @return
* The width of the widest label on the Y (vertical) axis.
*/
protected abstract int getYLabelWidth(Graphics2D g);
protected abstract String[] getXLabels();
protected int getXLabelHeight() {
int result = 0;
if(this.showTopLabels == true || this.showBottomLabels == true) {
String[] labels = this.getXLabels();
int labelHeight = 0;
if(labels != null && labels.length > 0) {
labelHeight =
this.tickMarkHeight +
( (labels != null)
? ChartGraphics.getStringHeight(
labels[0], this.m_metricsLabel)
: this.m_metricsLabel.getAscent() );
} else {
labelHeight = this.tickMarkHeight +
m_metricsLabel.getAscent();
}
if(this.showTopLabels == true)
result += labelHeight;
if(this.showBottomLabels == true)
result += labelHeight;
}
return result;
}
protected abstract Rectangle getInteriorRectangle(ChartGraphics g);
protected int getExteriorHeight() {
int cyLabel = this.getXLabelHeight();
int cyLegend = this.getXLegendHeight();
int cyBuf = (m_metricsLabel.getAscent() / 2);
// Provide a little extra space if there is no bottom label or legend
if(this.showBottomLabels == false && this.showBottomLegend == false)
cyBuf += (m_metricsLabel.getAscent() / 2);
return ( this.topBorder + cyLegend + cyLabel + cyBuf + this.bottomBorder );
}
protected Collection initData(Collection coll) {
return coll;
}
protected void initFonts() {
// Initialize FontMetrics
Image img = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g = (Graphics2D)img.getGraphics();
m_metricsLabel = g.getFontMetrics(this.font);
m_metricsLegend = g.getFontMetrics(this.legendFont);
g.dispose();
}
protected void preInit() {
// Create the ChartGraphics
Collection coll = this.initData(this.m_collDataPointColl);
if(coll instanceof ArrayList)
this.m_collDataPointColl = (ArrayList)coll;
else
throw new ClassCastException("initData() must return a collection of type ArrayList.");
// Set the variable width and height
if(this.height == Chart.VARIABLE_HEIGHT) this.height = this.calcVariableHeight();
if(this.width == Chart.VARIABLE_WIDTH) this.width = this.calcVariableWidth();
}
protected void postInit(Graphics2D g) {
// Calculate the base part of the image
this.calcRanges();
this.calc(g);
}
/**
* Retrieves the value and unit coordinate for a data point in the chart.
*
* @param valuePixels
* An int that specifies the size of the value axis in pixels.
* @param unitPixels
* An int that specifies the size of the unit axis in pixels.
* @param datapoint
* An int that specifies the zero-based index of the datum to
* calculate coordinates for.
* @param collection
* The DataPointCollection the datapoint is located in.
*
* @return
* A java.awt.Point object that contains the X and Y coordinate.
*
* @see java.awt.Rectangle
*/
protected Point getDataPoint(int valuePixels, int unitPixels, int datapoint,
DataPointCollection coll)
{
int cDataPts = coll.size();
double dVal = ((IDisplayDataPoint) coll.get(datapoint)).getValue();
return this.getDisplayPoint(valuePixels, unitPixels, cDataPts, dVal,
datapoint);
}
/**
* Retrieves the value and unit coordinate for a value on the x and y axis
* in the chart.
*
* @param valuePixels
* An int that specifies the size of the value axis in pixels.
* @param unitPixels
* An int that specifies the size of the unit axis in pixels.
* @param unitPoints
* Number of ticks on the unit axis.
* @param value
* A double that specifies the value to get coordinates for.
* @param unitIndex
* Ticks on the unit axis to get coordinates for.
*
* @return
* A java.awt.Point object that contains the X and Y coordinate.
*/
protected Point getDisplayPoint(int valuePixels, int unitPixels,
int unitPoints, double value,
int unitIndex) {
int iSpread = unitPixels - (this.valueIndent * 2);
iSpread = (unitPoints > 1) ? (iSpread / (unitPoints - 1)) : iSpread;
if (Double.isNaN(value) == true)
return null;
if (this.ceiling != this.floor) {
if (value < this.floor) {
// This is a problem, set value to the floor
log.error("Data point value (" + value + ") lower than floor ("
+ this.floor + ")");
value = this.floor;
} else if (value > this.ceiling) {
// Could be availability, where ceiling is 100% and unknown is 2
if (ceiling == 1 && value == 2)
return null;
// This is a problem, set value to the ceiling
log.error("Data point value (" + value +
") higher than ceiling (" + this.ceiling + ")");
value = this.ceiling;
}
}
// X = unitPixels, Y = valuePixels
int x = this.valueIndent + (iSpread * unitIndex);
int y = valuePixels - (int) Math.round(
(value - this.m_floor) * this.scale(valuePixels));
if (x == 0)
x++;
else if (x == (unitPixels - this.lineWidth))
x--;
if (y == 0)
y++;
else if (y == valuePixels)
y--;
return new Point(x, y);
}
protected String getUnitLabel(IDisplayDataPoint data) {
return data.getLabel();
}
protected boolean hasData() {
return (this.m_bNoData == false);
}
/**
* Calculates the scale of the graph data points. This is the number of
* pixels per data point.
*
* @param height
* The height of the rectangle to calculate the vertical scale for.
*
* @return
* A floating point value that specifies the scale multiplier of the vertical axis.
*/
protected double scale(int height) {
double result = (height / (this.m_adRangeMarks[this.m_adRangeMarks.length - 1] -
this.m_adRangeMarks[0]));
return result;
}
protected Class getDataCollectionClass() {
return DataPointCollection.class;
}
protected boolean checkHighLow() {
return false;
}
// protected IndexColorModel getIndexColorModel() {
// return (IndexColorModel)(new BufferedImage(
// 1, 1, BufferedImage.TYPE_BYTE_INDEXED)).getColorModel();
// }
////////////////////////////////////////////////
// Properties
/**
* Retrieves the average value of the chart's data set.
*
* @return
* A floating point value that is the average value of the chart's
* data set.
*/
public double getAverageValue() {
return this.m_dAvgValue;
}
public String getNoDataString() {
return this.m_strNoData;
}
public void setNoDataString(String s) {
this.m_strNoData = (s == null) ? EMPTY_STRING : s;
}
/**
* Retrieves the data set of the chart.
*
* @return
* A net.hyperic.chart.DataPointCollection object that contains the
* current data set points.
*
* @see net.hyperic.chart.DataPointCollection
*/
public DataPointCollection getDataPoints() {
return this.getDataPoints(0);
}
public EventPointCollection getEventPoints() {
return this.getEventPoints(0);
}
public DataPointCollection getDataPoints(int index) {
return (DataPointCollection)this.m_collDataPointColl.get(index);
}
public EventPointCollection getEventPoints(int index) {
return (EventPointCollection)this.m_collEvtPointColl.get(index);
}
public int getDataSetCount() {
return this.m_collDataPointColl.size();
}
public Iterator getDataSetIterator() {
return this.m_collDataPointColl.iterator();
}
public Iterator getEventSetIterator() {
return this.m_collEvtPointColl.iterator();
}
public void setNumberDataSets(int number) {
int delta = number - m_collDataPointColl.size();
if(delta > 0) {
try {
for(int i = 0;i < delta;i++) {
m_collDataPointColl.add(
this.getDataCollectionClass().newInstance() );
m_collEvtPointColl.add( new EventPointCollection() );
}
} catch(Exception e) {
System.out.println(e);
}
} else if(delta < 0) {
for(int i = delta;i < 0;i++) {
m_collDataPointColl.remove(m_collDataPointColl.size() - 1);
m_collEvtPointColl.remove(m_collEvtPointColl.size()- 1);
}
}
// Make sure we allways have at least one empty collection
if(this.getDataSetCount() == 0) {
try {
m_collDataPointColl.add(
this.getDataCollectionClass().newInstance() );
} catch(Exception e) {
System.out.println(e);
}
}
}
public void setFormat(int type, int scale) {
this.m_fmtType = type;
this.m_fmtScale = scale;
}
public Rectangle getExteriorRectangle() {
return new Rectangle(this.xOffset, this.yOffset, this.width, this.height);
}
/**
* Retrieves the low value of the chart's data set.
*
* @return
* A floating point value that is the low value of the chart's
* data set.
*/
public double getLowValue() {
return this.m_dLowValue;
}
/**
* Retrieves the peak value of the chart's data set.
*
* @return A floating point value that is the peak value of the chart's
* data set.
*/
public double getPeakValue() {
return this.m_dPeakValue;
}
/**
* Get the chart's title.
*
* @return A string containing the chart's title.
*/
public String getTitle() {
return this.m_strTitle;
}
/**
* Set the chart's title.
*
* @param title A string containing the chart's title.
*/
public void setTitle(String title) {
this.m_strTitle = ( (title == null) ? Chart.EMPTY_STRING : title );
}
/**
* Retrieves the legend for the chart's Unit axis. The default legend is
* "TIME".
*
* @return
* A java.lang.String object that contains the chart's Unit axis
* lagend.
*/
public String getUnitLegend() {
return this.m_strUnitLegend;
}
/**
* Sets the legend for the chart's Unit axis.
*
* @param label
* A java.lang.String object that contains the chart's Unit axis
* legend.
*
* @exception IllegalArgumentException
* If the legend parameter is null.
*/
public void setUnitLegend(String legend) {
this.m_strUnitLegend = ( (legend == null) ? Chart.EMPTY_STRING : legend );
}
/**
* Retrieves the legend for the chart's Value axis. The default legend is
* "VALUE".
*
* @return
* A java.lang.String object that contains the chart's Value axis
* legend.
*/
public String getValueLegend() {
return this.m_strValueLegend;
}
/**
* Sets the legend for the chart's Value axis.
*
* @param label
* A java.lang.String object that contains the chart's Value axis
* legend.
*
* @exception IllegalArgumentException
* If the legend parameter is null.
*/
public void setValueLegend(String legend) {
this.m_strValueLegend = ( (legend == null) ? Chart.EMPTY_STRING : legend );
}
public void setAbsTimeLabels (boolean useAbsTimeLabels, long interval) {
m_useAbsTimeLabels = useAbsTimeLabels;
m_smartLabelMaker = new SmartLabelMaker(interval);
}
}