package com.ibm.nmon.gui.report;
import org.slf4j.Logger;
import javax.swing.JFrame;
import javax.swing.ButtonGroup;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JRadioButton;
import javax.swing.ListSelectionModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JSplitPane;
import javax.swing.JOptionPane;
import javax.swing.JFileChooser;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import com.ibm.nmon.data.DataSet;
import com.ibm.nmon.data.DataSetListener;
import com.ibm.nmon.gui.main.NMONVisualizerGui;
import com.ibm.nmon.gui.chart.BaseChartPanel;
import com.ibm.nmon.gui.chart.annotate.AnnotationCache;
import com.ibm.nmon.gui.chart.annotate.AnnotationListener;
import com.ibm.nmon.gui.file.GUIFileChooser;
import com.ibm.nmon.gui.Styles;
/**
* Window for displaying a custom set of charts. Includes a list of processed datasets (systems) on the left and a chart
* on the right.
*/
public final class ReportFrame extends JFrame implements DataSetListener, PropertyChangeListener, AnnotationListener {
private static final long serialVersionUID = -2870624156130798498L;
private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(ReportFrame.class);
private final NMONVisualizerGui gui;
private ReportSplitPane reportSplitPane;
private final JList<DataSet> systems;
// if adding an annotation, ignore events from AnnotationCache
private boolean addingAnnotation = false;
public ReportFrame(NMONVisualizerGui gui) {
super("Custom Report");
this.gui = gui;
this.reportSplitPane = new ReportSplitPane(gui, this);
this.reportSplitPane.addPropertyChangeListener(this);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setResizable(true);
setIconImage(Styles.REPORT_ICON.getImage());
// Centered in the main frame at 90% of its size
Dimension parentSize = gui.getMainFrame().getSize();
Dimension thisSize = new Dimension((int) (parentSize.getWidth() * 0.9), (int) (parentSize.getHeight() * 0.9));
setSize(thisSize);
setLocationRelativeTo(gui.getMainFrame());
// maximize if main frame is also maximized
// size and location will still be set and remembered as above
if ((gui.getMainFrame().getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) {
setExtendedState(getExtendedState() | JFrame.MAXIMIZED_BOTH);
}
setJMenuBar(new ReportMenu(gui, this));
ReportSystemsListModel model = new ReportSystemsListModel();
systems = new JList<DataSet>(model);
systems.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
systems.setCellRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = -5095717717027686318L;
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
// see ReportSystemsListModel, which manages the indexing
if (index == 0) {
setText("All Systems");
setIcon(Styles.REPORT_ICON);
}
else {
setText(((DataSet) value).getHostname());
setIcon(Styles.COMPUTER_ICON);
}
setBorder(new EmptyBorder(2, 5, 2, 5));
return this;
}
});
systems.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
DataSet selected = systems.getModel().getElementAt(
systems.getSelectionModel().getMinSelectionIndex());
if (selected == null) {
reportSplitPane.setData(ReportFrame.this.gui.getDataSets());
enableMultiplexing(false);
}
else {
reportSplitPane.setData(java.util.Collections.singletonList(selected));
enableMultiplexing(true);
}
}
}
});
for (DataSet data : gui.getDataSets()) {
model.addData(data);
}
JRadioButton none = new JRadioButton("None");
JRadioButton byType = new JRadioButton("By Type");
JRadioButton byField = new JRadioButton("By Field");
none.setActionCommand(ReportPanel.MultiplexMode.NONE.name());
byType.setActionCommand(ReportPanel.MultiplexMode.BY_TYPE.name());
byField.setActionCommand(ReportPanel.MultiplexMode.BY_FIELD.name());
none.setFont(Styles.LABEL);
byType.setFont(Styles.LABEL);
byField.setFont(Styles.LABEL);
none.setBorder(Styles.CONTENT_BORDER);
byType.setBorder(Styles.CONTENT_BORDER);
byField.setBorder(Styles.CONTENT_BORDER);
ActionListener modeChanger = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
reportSplitPane.setMultiplexMode(ReportPanel.MultiplexMode.valueOf(e.getActionCommand()));
}
};
none.addActionListener(modeChanger);
byType.addActionListener(modeChanger);
byField.addActionListener(modeChanger);
none.setSelected(true);
ButtonGroup group = new ButtonGroup();
group.add(none);
group.add(byType);
group.add(byField);
JLabel multiplex = new JLabel("Chart Multiplexing:");
multiplex.setFont(Styles.LABEL);
JPanel top = new JPanel();
top.add(multiplex);
top.add(none);
top.add(byType);
top.add(byField);
JPanel right = new JPanel(new BorderLayout());
right.add(top, BorderLayout.PAGE_START);
right.add(reportSplitPane, BorderLayout.CENTER);
// tree of parsed files on the left, content on the left
JSplitPane lrSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
// never resize list automatically
lrSplitPane.setResizeWeight(0);
lrSplitPane.setBorder(null);
lrSplitPane.setLeftComponent(systems);
lrSplitPane.setRightComponent(right);
setContentPane(lrSplitPane);
systems.setSelectedIndex(0);
enableMultiplexing(false);
gui.addDataSetListener(this);
AnnotationCache.addAnnotationListener(this);
addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
// can only update the divider locations when the window is visible
// list gets 15%; charts get 70%
((JSplitPane) getContentPane()).setDividerLocation(.15);
reportSplitPane.setDividerLocation(.7);
}
});
}
@Override
public void dataAdded(DataSet data) {
if (systems.getSelectedIndex() == 0) {
// update All Systems chart
reportSplitPane.setData(gui.getDataSets());
}
((ReportSystemsListModel) systems.getModel()).addData(data);
}
@Override
public void dataRemoved(DataSet data) {
if (systems.getSelectedIndex() == 0) {
// update All Systems chart
reportSplitPane.setData(gui.getDataSets());
}
else {
if (systems.getSelectedValue().equals(data)) {
// data removed, back to All Systems
systems.setSelectedIndex(0);
}
}
((ReportSystemsListModel) systems.getModel()).removeData(data);
}
@Override
public void dataChanged(DataSet data) {
((ReportSystemsListModel) systems.getModel()).dataChanged();
}
@Override
public void dataCleared() {
if (systems.getSelectedIndex() == 0) {
// update All Systems chart
reportSplitPane.setData(gui.getDataSets());
}
else {
systems.setSelectedIndex(0);
}
((ReportSystemsListModel) systems.getModel()).clearData();
}
private void enableMultiplexing(boolean enable) {
JSplitPane lrSplitPane = (JSplitPane) getContentPane();
JPanel top = (JPanel) ((JPanel) lrSplitPane.getRightComponent()).getComponent(0);
for (int i = 1; i < 4; i++) {
top.getComponent(i).setEnabled(enable);
}
}
@Override
public void dispose() {
reportSplitPane.dispose();
gui.removeDataSetListener(this);
AnnotationCache.removeAnnoationListener(this);
super.dispose();
}
public void propertyChange(java.beans.PropertyChangeEvent evt) {
if ("chart".equals(evt.getPropertyName())) {
if (AnnotationCache.hasAnnotations()) {
if (evt.getNewValue() != null) {
addingAnnotation = true;
((BaseChartPanel) evt.getNewValue()).addAnnotations(AnnotationCache.getAnnotations());
((BaseChartPanel) evt.getNewValue()).addMarkers(AnnotationCache.getMarkers());
addingAnnotation = false;
}
}
}
else if ("annotation".equals(evt.getPropertyName())) {
if (evt.getNewValue() != null) {
addingAnnotation = true;
AnnotationCache.add(evt.getNewValue());
addingAnnotation = false;
}
}
}
@Override
public void annotationAdded() {
annotationsCleared();
}
@Override
public void annotationRemoved() {
annotationsCleared();
}
@Override
public void annotationsCleared() {
if (!addingAnnotation) {
BaseChartPanel currentChart = reportSplitPane.getChartPanel();
if (currentChart != null) {
addingAnnotation = true;
currentChart.addAnnotations(AnnotationCache.getAnnotations());
currentChart.addMarkers(AnnotationCache.getMarkers());
addingAnnotation = false;
}
}
}
NMONVisualizerGui getGui() {
return gui;
}
boolean loadReportDefinition(File reportFile) {
if (!reportFile.exists()) {
JOptionPane.showMessageDialog(this, "File '" + reportFile.getName() + "' is not a valid file",
"Invalid File", JOptionPane.ERROR_MESSAGE);
// existing report => true
return reportSplitPane.hasReport();
}
try {
reportSplitPane.loadReport(reportFile);
setTitle("Custom Report" + " - " + reportFile.getName());
configureReport();
return true;
}
catch (Exception e) {
setTitle("Custom Report");
LOGGER.error("could not parse report file '{}'", reportFile.getAbsolutePath(), e);
JOptionPane.showMessageDialog(this, "Error parsing '" + reportFile.getName() + "'\n" + e.getMessage(),
"Parse Error", JOptionPane.ERROR_MESSAGE);
// reportSplitPane.clearReport();
return false;
}
}
void loadDefaultDataSetReport() {
reportSplitPane.loadDefaultDataSetReport();
setTitle("Default DataSet Report");
configureReport();
}
void loadDefaultSummaryReport() {
reportSplitPane.loadDefaultSummaryReport();
setTitle("Default All Systems Report");
configureReport();
}
void loadDefaultIOStatReport() {
reportSplitPane.loadDefaultIOStatReport();
setTitle("Default IOStat Report");
configureReport();
}
void loadDefaultIOStatDiskDataReport() {
reportSplitPane.loadDefaultIOStatDiskDataReport();
setTitle("Default IOStat Disk Data Report");
configureReport();
}
private void configureReport() {
// set the report's data
DataSet selected = systems.getModel().getElementAt(systems.getSelectionModel().getMinSelectionIndex());
if (selected == null) {
reportSplitPane.setData(gui.getDataSets());
}
else {
reportSplitPane.setData(java.util.Collections.singletonList(selected));
}
}
void saveAllCharts() {
GUIFileChooser chooser = new GUIFileChooser(gui, "Select Save Location");
chooser.setFileSelectionMode(GUIFileChooser.DIRECTORIES_ONLY);
chooser.setMultiSelectionEnabled(false);
if (chooser.showDialog(this, "Save") == JFileChooser.APPROVE_OPTION) {
String directory = chooser.getSelectedFile().getAbsolutePath();
reportSplitPane.saveCharts(directory);
}
}
}