/*
*-------------------
* The StackedChartDetail.java is part of ASH Viewer
*-------------------
*
* ASH Viewer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ASH Viewer 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 ASH Viewer. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (c) 2009, Alex Kardapolov, All rights reserved.
*
*/
package org.ash.detail;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.ash.database.ASHDatabase;
import org.ash.util.Options;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.block.BlockContainer;
import org.jfree.chart.block.BorderArrangement;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYAreaRenderer3;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.xy.CategoryTableXYDataset;
import org.jfree.ui.HorizontalAlignment;
import org.jfree.ui.LengthAdjustmentType;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.TextAnchor;
import com.sleepycat.je.DatabaseException;
/**
* The Class StackedXYAreaChartDetail.
*/
public class StackedChartDetail {
/** The database. */
private ASHDatabase database;
/** The dataset. */
private CategoryTableXYDataset dataset;
/** The renderer. */
private StackedXYAreaRenderer3 renderer;
/** The chart. */
private JFreeChart chart;
/** The chart panel. */
private ChartPanel chartPanel;
/** The date format. */
private DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");
/** The current date. */
private Date currentDate;
/** The x axis. */
private DateAxis xAxis;
/** The threshold max cpu. */
private ValueMarker thresholdMaxCpu;
/** The max cpu. */
private double maxCpu;
/** The plot. */
private XYPlot plot;
/** The current end. */
private Marker currentEnd;
/** The flag threshold begin time auto selection. */
private boolean flagThresholdBeginTimeAutoSelection = false;
/** The Active Session Working*/
private String activeSessionWorking = "Active Session Working: ";
/** The Active Session Waiting*/
private String activeSessionWaiting = "Active Session Waiting: ";
/** The cpu. */
private String CPU = "CPU used";
/** The waitClass or CPU used */
private String waitClass = "none";
/** The store of series(int) and labels of wait event (details)*/
private LinkedHashMap<Integer, String> seriesIdName;
/** The store labels of wait event for sum (details)*/
private HashMap<String, Double> seriesNameSum;
/**
* Instantiates a new stacked xy area chart.
*
* @param database0 the database0
*/
public StackedChartDetail(ASHDatabase database0, String waitClass0) {
this.database = database0;
this.waitClass = waitClass0;
this.seriesIdName = new LinkedHashMap<Integer, String>();
this.seriesNameSum = new HashMap<String, Double>();
}
/**
* Creates the chart panel.
*
* @return the chart panel
*
* @throws DatabaseException the database exception
*/
public ChartPanel createChartPanel(){
dataset = new CategoryTableXYDataset();
JFreeChart chart = createChart();
chartPanel = new ChartPanel(chart);
chartPanel.setRangeZoomable(false);
return chartPanel;
}
/**
* Sets the threshold begin time auto selection.
*
* @param beginTime the begin time
* @param range the range
*/
public synchronized void setThresholdBeginTimeAutoSelection(double beginTime, int range){
plot.removeDomainMarker(currentEnd);
currentEnd = new ValueMarker(beginTime);
currentEnd.setPaint(Color.red);
currentEnd.setLabel(range+" min");
currentEnd.setStroke(new BasicStroke(1.0f));
currentEnd.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
currentEnd.setLabelTextAnchor(TextAnchor.TOP_LEFT);
plot.addDomainMarker(currentEnd);
}
/**
* Update xaxis label.
*
* @param time the time
*/
public void updatexAxisLabel(double time){
if (!xAxis.getLabel().equalsIgnoreCase(dateFormat.format(time))){
xAxis.setLabel(dateFormat.format(time));
}
}
/**
* Sets the threshold max cpu.
*
* @param maxCpu the new threshold max cpu
*/
public void setThresholdMaxCpu(double maxCpu){
this.maxCpu = maxCpu;
this.setMarkerMaxCpu();
}
/**
* Sets the flag threshold begin time auto selection.
*
* @param flag0 the new flag threshold begin time auto selection
*/
public void setFlagThresholdBeginTimeAutoSelection(boolean flag0){
this.flagThresholdBeginTimeAutoSelection = flag0;
}
/**
* Checks if is flag threshold begin time auto selection.
*
* @return true, if is flag threshold begin time auto selection
*/
public boolean isFlagThresholdBeginTimeAutoSelection(){
return this.flagThresholdBeginTimeAutoSelection;
}
/**
* Removes the threshold begin time auto selection.
*/
public void removeThresholdBeginTimeAutoSelection(){
plot.removeDomainMarker(currentEnd);
}
/**
* Adds the listener chart panel.
*
* @param l the l
*/
public void addListenerChartPanel(Object l){
chartPanel.addListenerReleaseMouse(l);
}
/**
* Removes the listener chart panel.
*
* @param l the l
*/
public void removeListenerChartPanel(Object l){
chartPanel.removeListenerReleaseMouse(l);
}
/**
* Sets the selection chart.
*
* @param flag the new selection chart
*/
public void setSelectionChart(boolean flag){
chartPanel.setDomainZoomable(flag);
}
/**
* Checks if is mouse dragged.
*
* @return true, if is mouse dragged
*/
public boolean isMouseDragged(){
return chartPanel.isMouseDragged();
}
/**
* Set upper bound of range axis
*
* @param bound
*/
public void setUpperBoundOfRangeAxis(double bound){
if (bound == 0.0){
plot.getRangeAxis().setLowerBound(0.0);
plot.getRangeAxis().setAutoRange(true);
}
else {
plot.getRangeAxis().setAutoRange(false);
plot.getRangeAxis().setUpperBound(bound*this.maxCpu);
}
}
/***
* Load data to dataset
*
* @param waitClass - wait class or CPU used
*/
public void setTitle() {
if (this.waitClass.equalsIgnoreCase(CPU)){
chart.setTitle(this.activeSessionWorking
+this.waitClass);
}
else {
chart.setTitle(this.activeSessionWaiting
+this.waitClass);
}
}
/**
* Save series id and name
*
* @param index
* @param waitClass
*/
public void setSeriesIdName(int index, String waitClass){
this.seriesIdName.put(index, waitClass);
}
/**
* Save sum of series
*
* @param eventName
* @param sum
*/
public void setSeriesNameSum(String eventName, Double sum){
this.seriesNameSum.put(eventName, sum);
}
/**
* Get current sum value of event
*
* @param waitClass
* @return
*/
public Double getSeriesNameSum(String event){
return this.seriesNameSum.get(event);
}
/**
* Return true if event name already exist in seriesNameSum
*
* @param event
* @return
*/
public boolean isSeriesContainName(String event){
return this.seriesNameSum.containsKey(event);
}
/**
* Is seriesIdName is empty
*
* @return
*/
public boolean isSeriesIdNameEmpty(){
return this.seriesIdName.isEmpty();
}
/**
* Is dataset empty
*
* @return
*/
public boolean isDatasetEmpty(){
if (this.dataset.getSeriesCount()==0){
return true;
} else {
return false;
}
}
/**
* Return size of series id name
*
* @return
*/
public int getSizeSeriesIdName(){
return this.seriesIdName.size();
}
/**
* Set series paint
*
* @param series
* @param waitClass
*/
public void setSeriesPaint(int series, String waitClass, String from){
if (waitClass != null){
this.renderer.setSeriesPaint(series,
Options.getInstance().getColor(waitClass), true);
}
}
/**
* Delete values from dataset (details)
*
*/
public void deleteValuesFromDatasetDetail(Double beginRange){
// Clear values from dataset when it's not empty
if (this.renderer.getLegendItems().getItemCount()!=0){
for (int i=0;i<50;i++) {
Double xValue = (Double)dataset.getX(0, i);
if(xValue>beginRange){
break;
}
try {
dataset.removeRow(xValue);
} catch (Exception e){
e.printStackTrace();
}
}
}
}
/**
* Calculate sum, save data to dataset and clear temp. array
*
* @param rangeHalf delay/2
* @param dd current time
*/
public void calcSaveAndClear(int rangeHalf, double dd){
int rangeHalfSec = (rangeHalf*2)/1000;
double sumEvent = 0.0;
Set entries = this.seriesNameSum.entrySet();
Iterator iter = entries.iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
sumEvent = sumEvent + (Double)entry.getValue();
}
if (sumEvent == 0.0){
sumEvent = 0.000001;
}
// Save System Event and CPU statistics
Set entrSeriesIdName = this.seriesIdName.entrySet();
Iterator iterSIM = entrSeriesIdName.iterator();
while (iterSIM.hasNext()) {
Map.Entry entry = (Map.Entry) iterSIM.next();
String eventName = (String)entry.getValue();
Double tmpValue =
(((Double)this.seriesNameSum.get(eventName)/sumEvent)*sumEvent)/rangeHalfSec;
if (eventName != null){
this.dataset.add(dd+rangeHalf, tmpValue, eventName);
}
}
// Clear values
Set entrClear = this.seriesIdName.entrySet();
Iterator iterClear = entrClear.iterator();
while (iterClear.hasNext()) {
Map.Entry entry = (Map.Entry) iterClear.next();
String eventName = (String)entry.getValue();
this.seriesNameSum.put(eventName, 0.0);
}
}
/**
* Add points to left side.
*
* @param rangeHalf delay/2
* @param dd current time
*/
public void addPointsToLeft(double beginTime, double endTime, int rangeHalf){
int rangeHalfSec = (rangeHalf*2)/1000;
// Save System Event and CPU statistics
Set entrSeriesIdName = this.seriesIdName.entrySet();
Iterator iterSIM = entrSeriesIdName.iterator();
while (iterSIM.hasNext()) {
Map.Entry entry = (Map.Entry) iterSIM.next();
String eventName = (String)entry.getValue();
Double tmpValue = 0.0;
for (double dd = beginTime; dd < endTime; dd += rangeHalf * 2) {
if (eventName != null){
this.dataset.add(dd+rangeHalf, tmpValue, eventName);
}
}
}
}
/**
* Get BufferedImage from chart
*
* @return
*/
public BufferedImage createBufferedImage(
int imageWidth, int imageHeight,
double drawWidth, double drawHeight,
ChartRenderingInfo info){
return chart.createBufferedImage(
imageWidth, imageHeight,
drawWidth, drawHeight,
info);
}
/**
* Creates the chart.
*
* @return the jfreechart
*/
private JFreeChart createChart() {
xAxis = new DateAxis("time");
xAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
currentDate = new Date();
updatexAxisLabel(new Long(currentDate.getTime()).doubleValue());
chart = ChartFactory.createStackedXYAreaChart(
"", // chart title
"X Value", // domain axis label
"Active Sessions", // range axis label
dataset, // data
PlotOrientation.VERTICAL, // the plot orientation
xAxis, // xAxis
false, // legend
true, // tooltips
false // urls
);
plot = (XYPlot) chart.getPlot();
renderer = new StackedXYAreaRenderer3();
renderer.setRoundXCoordinates(true);
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator
("{0} ({1}, {2})",
new SimpleDateFormat("HH:mm"),
new DecimalFormat("0.0")));
plot.setRenderer(0, renderer);
plot.getRangeAxis().setLowerBound(0.0);
plot.getRangeAxis().setAutoRange(true);
// Set format for x axis
DateAxis axis = (DateAxis) plot.getDomainAxis();
axis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
// Add legend to right
LegendTitle legend = new LegendTitle(chart.getPlot());
BlockContainer wrapper = new BlockContainer(new BorderArrangement());
wrapper.setFrame(new BlockBorder(1.0, 1.0, 1.0, 1.0));
BlockContainer itemss = legend.getItemContainer();
itemss.setPadding(2, 10, 5, 2);
wrapper.add(itemss);
legend.setWrapper(wrapper);
legend.setPosition(RectangleEdge.RIGHT);
legend.setHorizontalAlignment(HorizontalAlignment.LEFT);
chart.addSubtitle(legend);
return chart;
}
/**
* Add a marker Maximum CPU
*
*/
private void setMarkerMaxCpu(){
// add a labelled marker for the cpu_count
thresholdMaxCpu = new ValueMarker(this.maxCpu);
thresholdMaxCpu.setLabelOffsetType(LengthAdjustmentType.EXPAND);
thresholdMaxCpu.setPaint(Color.red);
thresholdMaxCpu.setStroke(new BasicStroke(1.0f));
thresholdMaxCpu.setLabel("Maximum CPU");
thresholdMaxCpu.setLabelFont(new Font("SansSerif", Font.PLAIN, 11));
thresholdMaxCpu.setLabelPaint(Color.red);
thresholdMaxCpu.setLabelAnchor(RectangleAnchor.TOP_LEFT);
thresholdMaxCpu.setLabelTextAnchor(TextAnchor.BOTTOM_LEFT);
plot.addRangeMarker(thresholdMaxCpu);
}
}