public class

XSheet

extends JPanel
implements ActionListener NotificationListener
/*
 * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.tools.jconsole.inspector;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.Enumeration;
import javax.management.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.tree.*;
import sun.tools.jconsole.*;
import sun.tools.jconsole.inspector.XNodeInfo.Type;

import static sun.tools.jconsole.Resources.*;
import static sun.tools.jconsole.Utilities.*;

@SuppressWarnings("serial")
public class XSheet extends JPanel
        implements ActionListener, NotificationListener {

    private JPanel mainPanel;
    private JPanel southPanel;

    // Node being currently displayed
    private DefaultMutableTreeNode node;

    // MBean being currently displayed
    private XMBean mbean;

    // XMBeanAttributes container
    private XMBeanAttributes mbeanAttributes;

    // XMBeanOperations container
    private XMBeanOperations mbeanOperations;

    // XMBeanNotifications container
    private XMBeanNotifications mbeanNotifications;

    // XMBeanInfo container
    private XMBeanInfo mbeanInfo;

    // Refresh JButton (mbean attributes case)
    private JButton refreshButton;

    // Subscribe/Unsubscribe/Clear JButton (mbean notifications case)
    private JButton clearButton, subscribeButton, unsubscribeButton;

    // Reference to MBeans tab
    private MBeansTab mbeansTab;

    public XSheet(MBeansTab mbeansTab) {
        this.mbeansTab = mbeansTab;
        setupScreen();
    }

    public void dispose() {
        clear();
        XDataViewer.dispose(mbeansTab);
        mbeanNotifications.dispose();
    }

    private void setupScreen() {
        setLayout(new BorderLayout());
        // add main panel to XSheet
        mainPanel = new JPanel();
        mainPanel.setLayout(new BorderLayout());
        add(mainPanel, BorderLayout.CENTER);
        // add south panel to XSheet
        southPanel = new JPanel();
        add(southPanel, BorderLayout.SOUTH);
        // create the refresh button
        String refreshButtonKey = "MBeansTab.refreshAttributesButton";
        refreshButton = new JButton(getText(refreshButtonKey));
        refreshButton.setMnemonic(getMnemonicInt(refreshButtonKey));
        refreshButton.setToolTipText(getText(refreshButtonKey + ".toolTip"));
        refreshButton.addActionListener(this);
        // create the clear button
        String clearButtonKey = "MBeansTab.clearNotificationsButton";
        clearButton = new JButton(getText(clearButtonKey));
        clearButton.setMnemonic(getMnemonicInt(clearButtonKey));
        clearButton.setToolTipText(getText(clearButtonKey + ".toolTip"));
        clearButton.addActionListener(this);
        // create the subscribe button
        String subscribeButtonKey = "MBeansTab.subscribeNotificationsButton";
        subscribeButton = new JButton(getText(subscribeButtonKey));
        subscribeButton.setMnemonic(getMnemonicInt(subscribeButtonKey));
        subscribeButton.setToolTipText(getText(subscribeButtonKey + ".toolTip"));
        subscribeButton.addActionListener(this);
        // create the unsubscribe button
        String unsubscribeButtonKey = "MBeansTab.unsubscribeNotificationsButton";
        unsubscribeButton = new JButton(getText(unsubscribeButtonKey));
        unsubscribeButton.setMnemonic(getMnemonicInt(unsubscribeButtonKey));
        unsubscribeButton.setToolTipText(getText(unsubscribeButtonKey + ".toolTip"));
        unsubscribeButton.addActionListener(this);
        // create XMBeanAttributes container
        mbeanAttributes = new XMBeanAttributes(mbeansTab);
        // create XMBeanOperations container
        mbeanOperations = new XMBeanOperations(mbeansTab);
        mbeanOperations.addOperationsListener(this);
        // create XMBeanNotifications container
        mbeanNotifications = new XMBeanNotifications();
        mbeanNotifications.addNotificationsListener(this);
        // create XMBeanInfo container
        mbeanInfo = new XMBeanInfo();
    }

    public boolean isMBeanNode(DefaultMutableTreeNode node) {
        XNodeInfo uo = (XNodeInfo) node.getUserObject();
        return uo.getType().equals(Type.MBEAN);
    }

    public void displayNode(DefaultMutableTreeNode node) {
        clear();
        if (node == null) {
            displayEmptyNode();
            return;
        }
        Object userObject = node.getUserObject();
        if (userObject instanceof XNodeInfo) {
            XNodeInfo uo = (XNodeInfo) userObject;
            switch (uo.getType()) {
                case MBEAN:
                    displayMBeanNode(node);
                    break;
                case NONMBEAN:
                    displayEmptyNode();
                    break;
                case ATTRIBUTES:
                    displayMBeanAttributesNode(node);
                    break;
                case OPERATIONS:
                    displayMBeanOperationsNode(node);
                    break;
                case NOTIFICATIONS:
                    displayMBeanNotificationsNode(node);
                    break;
                case ATTRIBUTE:
                case OPERATION:
                case NOTIFICATION:
                    displayMetadataNode(node);
                    break;
                default:
                    displayEmptyNode();
                    break;
            }
        } else {
            displayEmptyNode();
        }
    }

    private void displayMBeanNode(final DefaultMutableTreeNode node) {
        final XNodeInfo uo = (XNodeInfo) node.getUserObject();
        if (!uo.getType().equals(Type.MBEAN)) {
            return;
        }
        mbeansTab.workerAdd(new Runnable() {
            public void run() {
                try {
                    XSheet.this.node = node;
                    XSheet.this.mbean = (XMBean) uo.getData();
                    mbeanInfo.addMBeanInfo(mbean, mbean.getMBeanInfo());
                } catch (Throwable ex) {
                    EventQueue.invokeLater(new ThreadDialog(
                            XSheet.this,
                            ex.getMessage(),
                            Resources.getText("Problem displaying MBean"),
                            JOptionPane.ERROR_MESSAGE));
                    return;
                }
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        invalidate();
                        mainPanel.removeAll();
                        mainPanel.add(mbeanInfo, BorderLayout.CENTER);
                        southPanel.setVisible(false);
                        southPanel.removeAll();
                        validate();
                        repaint();
                    }
                });
            }
        });
    }

    // Call on EDT
    private void displayMetadataNode(final DefaultMutableTreeNode node) {
        final XNodeInfo uo = (XNodeInfo) node.getUserObject();
        final XMBeanInfo mbi = mbeanInfo;
        switch (uo.getType()) {
            case ATTRIBUTE:
                mbeansTab.workerAdd(new Runnable() {
                    public void run() {
                        Object attrData = uo.getData();
                        XSheet.this.mbean = (XMBean) ((Object[]) attrData)[0];
                        final MBeanAttributeInfo mbai =
                                (MBeanAttributeInfo) ((Object[]) attrData)[1];
                        final XMBeanAttributes mba = mbeanAttributes;
                        try {
                            mba.loadAttributes(mbean, new MBeanInfo(
                                    null, null, new MBeanAttributeInfo[] {mbai},
                                    null, null, null));
                        } catch (Exception e) {
                            EventQueue.invokeLater(new ThreadDialog(
                                    XSheet.this,
                                    e.getMessage(),
                                    Resources.getText("Problem displaying MBean"),
                                    JOptionPane.ERROR_MESSAGE));
                            return;
                        }
                        EventQueue.invokeLater(new Runnable() {
                            public void run() {
                                invalidate();
                                mainPanel.removeAll();
                                JPanel attributePanel =
                                        new JPanel(new BorderLayout());
                                JPanel attributeBorderPanel =
                                        new JPanel(new BorderLayout());
                                attributeBorderPanel.setBorder(
                                        BorderFactory.createTitledBorder(
                                        Resources.getText("Attribute value")));
                                JPanel attributeValuePanel =
                                        new JPanel(new BorderLayout());
                                attributeValuePanel.setBorder(
                                        LineBorder.createGrayLineBorder());
                                attributeValuePanel.add(mba.getTableHeader(),
                                        BorderLayout.PAGE_START);
                                attributeValuePanel.add(mba,
                                        BorderLayout.CENTER);
                                attributeBorderPanel.add(attributeValuePanel,
                                        BorderLayout.CENTER);
                                JPanel refreshButtonPanel = new JPanel();
                                refreshButtonPanel.add(refreshButton);
                                attributeBorderPanel.add(refreshButtonPanel,
                                        BorderLayout.SOUTH);
                                refreshButton.setEnabled(true);
                                attributePanel.add(attributeBorderPanel,
                                        BorderLayout.NORTH);
                                mbi.addMBeanAttributeInfo(mbai);
                                attributePanel.add(mbi, BorderLayout.CENTER);
                                mainPanel.add(attributePanel,
                                        BorderLayout.CENTER);
                                southPanel.setVisible(false);
                                southPanel.removeAll();
                                validate();
                                repaint();
                            }
                        });
                    }
                });
                break;
            case OPERATION:
                Object operData = uo.getData();
                XSheet.this.mbean = (XMBean) ((Object[]) operData)[0];
                MBeanOperationInfo mboi =
                        (MBeanOperationInfo) ((Object[]) operData)[1];
                XMBeanOperations mbo = mbeanOperations;
                try {
                    mbo.loadOperations(mbean, new MBeanInfo(null, null, null,
                            null, new MBeanOperationInfo[] {mboi}, null));
                } catch (Exception e) {
                    EventQueue.invokeLater(new ThreadDialog(
                            XSheet.this,
                            e.getMessage(),
                            Resources.getText("Problem displaying MBean"),
                            JOptionPane.ERROR_MESSAGE));
                    return;
                }
                invalidate();
                mainPanel.removeAll();
                JPanel operationPanel = new JPanel(new BorderLayout());
                JPanel operationBorderPanel = new JPanel(new BorderLayout());
                operationBorderPanel.setBorder(BorderFactory.createTitledBorder(
                        Resources.getText("Operation invocation")));
                operationBorderPanel.add(new JScrollPane(mbo));
                operationPanel.add(operationBorderPanel, BorderLayout.NORTH);
                mbi.addMBeanOperationInfo(mboi);
                operationPanel.add(mbi, BorderLayout.CENTER);
                mainPanel.add(operationPanel, BorderLayout.CENTER);
                southPanel.setVisible(false);
                southPanel.removeAll();
                validate();
                repaint();
                break;
            case NOTIFICATION:
                Object notifData = uo.getData();
                invalidate();
                mainPanel.removeAll();
                mbi.addMBeanNotificationInfo((MBeanNotificationInfo) notifData);
                mainPanel.add(mbi, BorderLayout.CENTER);
                southPanel.setVisible(false);
                southPanel.removeAll();
                validate();
                repaint();
                break;
        }
    }

    private void displayMBeanAttributesNode(final DefaultMutableTreeNode node) {
        final XNodeInfo uo = (XNodeInfo) node.getUserObject();
        if (!uo.getType().equals(Type.ATTRIBUTES)) {
            return;
        }
        final XMBeanAttributes mba = mbeanAttributes;
        mbeansTab.workerAdd(new Runnable() {
            public void run() {
                try {
                    XSheet.this.node = node;
                    XSheet.this.mbean = (XMBean) uo.getData();
                    mba.loadAttributes(mbean, mbean.getMBeanInfo());
                } catch (Throwable ex) {
                    EventQueue.invokeLater(new ThreadDialog(
                            XSheet.this,
                            ex.getMessage(),
                            Resources.getText("Problem displaying MBean"),
                            JOptionPane.ERROR_MESSAGE));
                    return;
                }
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        invalidate();
                        mainPanel.removeAll();
                        JPanel borderPanel = new JPanel(new BorderLayout());
                        borderPanel.setBorder(BorderFactory.createTitledBorder(
                                Resources.getText("Attribute values")));
                        borderPanel.add(new JScrollPane(mba));
                        mainPanel.add(borderPanel, BorderLayout.CENTER);
                        // add the refresh button to the south panel
                        southPanel.removeAll();
                        southPanel.add(refreshButton, BorderLayout.SOUTH);
                        southPanel.setVisible(true);
                        refreshButton.setEnabled(true);
                        validate();
                        repaint();
                    }
                });
            }
        });
    }

    private void displayMBeanOperationsNode(final DefaultMutableTreeNode node) {
        final XNodeInfo uo = (XNodeInfo) node.getUserObject();
        if (!uo.getType().equals(Type.OPERATIONS)) {
            return;
        }
        final XMBeanOperations mbo = mbeanOperations;
        mbeansTab.workerAdd(new Runnable() {
            public void run() {
                try {
                    XSheet.this.node = node;
                    XSheet.this.mbean = (XMBean) uo.getData();
                    mbo.loadOperations(mbean, mbean.getMBeanInfo());
                } catch (Throwable ex) {
                    EventQueue.invokeLater(new ThreadDialog(
                            XSheet.this,
                            ex.getMessage(),
                            Resources.getText("Problem displaying MBean"),
                            JOptionPane.ERROR_MESSAGE));
                    return;
                }
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        invalidate();
                        mainPanel.removeAll();
                        JPanel borderPanel = new JPanel(new BorderLayout());
                        borderPanel.setBorder(BorderFactory.createTitledBorder(
                                Resources.getText("Operation invocation")));
                        borderPanel.add(new JScrollPane(mbo));
                        mainPanel.add(borderPanel, BorderLayout.CENTER);
                        southPanel.setVisible(false);
                        southPanel.removeAll();
                        validate();
                        repaint();
                    }
                });
            }
        });
    }

    private void displayMBeanNotificationsNode(
            final DefaultMutableTreeNode node) {
        final XNodeInfo uo = (XNodeInfo) node.getUserObject();
        if (!uo.getType().equals(Type.NOTIFICATIONS)) {
            return;
        }
        final XMBeanNotifications mbn = mbeanNotifications;
        mbeansTab.workerAdd(new Runnable() {
            public void run() {
                try {
                    XSheet.this.node = node;
                    XSheet.this.mbean = (XMBean) uo.getData();
                    mbn.loadNotifications(mbean);
                    updateNotifications();
                } catch (Throwable ex) {
                    EventQueue.invokeLater(new ThreadDialog(
                            XSheet.this,
                            ex.getMessage(),
                            Resources.getText("Problem displaying MBean"),
                            JOptionPane.ERROR_MESSAGE));
                    return;
                }
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        invalidate();
                        mainPanel.removeAll();
                        JPanel borderPanel = new JPanel(new BorderLayout());
                        borderPanel.setBorder(BorderFactory.createTitledBorder(
                                Resources.getText("Notification buffer")));
                        borderPanel.add(new JScrollPane(mbn));
                        mainPanel.add(borderPanel, BorderLayout.CENTER);
                        // add the subscribe/unsubscribe/clear buttons to
                        // the south panel
                        southPanel.removeAll();
                        southPanel.add(subscribeButton, BorderLayout.WEST);
                        southPanel.add(unsubscribeButton, BorderLayout.CENTER);
                        southPanel.add(clearButton, BorderLayout.EAST);
                        southPanel.setVisible(true);
                        subscribeButton.setEnabled(true);
                        unsubscribeButton.setEnabled(true);
                        clearButton.setEnabled(true);
                        validate();
                        repaint();
                    }
                });
            }
        });
    }

    // Call on EDT
    private void displayEmptyNode() {
        invalidate();
        mainPanel.removeAll();
        southPanel.removeAll();
        validate();
        repaint();
    }

    /**
     * Subscribe button action.
     */
    private void registerListener() throws InstanceNotFoundException,
            IOException {
        mbeanNotifications.registerListener(node);
        updateNotifications();
        validate();
    }

    /**
     * Unsubscribe button action.
     */
    private void unregisterListener() {
        if (mbeanNotifications.unregisterListener(node)) {
            clearNotifications();
            validate();
        }
    }

    /**
     * Refresh button action.
     */
    private void refreshAttributes() {
        mbeanAttributes.refreshAttributes();
    }

    private void updateNotifications() {
        if (mbean.isBroadcaster()) {
            if (mbeanNotifications.isListenerRegistered(mbean)) {
                long received =
                        mbeanNotifications.getReceivedNotifications(mbean);
                updateReceivedNotifications(node, received, false);
            } else {
                clearNotifications();
            }
        } else {
            clearNotifications();
        }
    }

    /**
     * Update notification node label in MBean tree: "Notifications[received]".
     */
    private void updateReceivedNotifications(
            DefaultMutableTreeNode emitter, long received, boolean bold) {
        String text = Resources.getText("Notifications") + "[" + received + "]";
        DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)
        mbeansTab.getTree().getLastSelectedPathComponent();
        if (bold && emitter != selectedNode) {
            text = "<html><b>" + text + "</b></html>";
        }
        updateNotificationsNodeLabel(emitter, text);
    }

    /**
     * Update notification node label in MBean tree: "Notifications".
     */
    private void clearNotifications() {
        updateNotificationsNodeLabel(node,
                Resources.getText("Notifications"));
    }

    /**
     * Update notification node label in MBean tree: "Notifications[0]".
     */
    private void clearNotifications0() {
        updateNotificationsNodeLabel(node,
                Resources.getText("Notifications") + "[0]");
    }

    /**
     * Update the label of the supplied MBean tree node.
     */
    private void updateNotificationsNodeLabel(
            final DefaultMutableTreeNode node, final String label) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                synchronized (mbeansTab.getTree()) {
                    invalidate();
                    XNodeInfo oldUserObject = (XNodeInfo) node.getUserObject();
                    XNodeInfo newUserObject = new XNodeInfo(
                            oldUserObject.getType(), oldUserObject.getData(),
                            label, oldUserObject.getToolTipText());
                    node.setUserObject(newUserObject);
                    DefaultTreeModel model =
                            (DefaultTreeModel) mbeansTab.getTree().getModel();
                    model.nodeChanged(node);
                    validate();
                    repaint();
                }
            }
        });
    }

    /**
     * Clear button action.
     */
    // Call on EDT
    private void clearCurrentNotifications() {
        mbeanNotifications.clearCurrentNotifications();
        if (mbeanNotifications.isListenerRegistered(mbean)) {
            // Update notifs in MBean tree "Notifications[0]".
            //
            // Notification buffer has been cleared with a listener been
            // registered so add "[0]" at the end of the node label.
            //
            clearNotifications0();
        } else {
            // Update notifs in MBean tree "Notifications".
            //
            // Notification buffer has been cleared without a listener been
            // registered so don't add "[0]" at the end of the node label.
            //
            clearNotifications();
        }
    }

    private void clear() {
        mbeanAttributes.stopCellEditing();
        mbeanAttributes.emptyTable();
        mbeanAttributes.removeAttributes();
        mbeanOperations.removeOperations();
        mbeanNotifications.stopCellEditing();
        mbeanNotifications.emptyTable();
        mbeanNotifications.disableNotifications();
        mbean = null;
        node = null;
    }

    /**
     * Notification listener: handles asynchronous reception
     * of MBean operation results and MBean notifications.
     */
    public void handleNotification(Notification e, Object handback) {
        // Operation result
        if (e.getType().equals(XOperations.OPERATION_INVOCATION_EVENT)) {
            final Object message;
            if (handback == null) {
                JTextArea textArea = new JTextArea("null");
                textArea.setEditable(false);
                textArea.setEnabled(true);
                textArea.setRows(textArea.getLineCount());
                message = textArea;
            } else {
                Component comp = mbeansTab.getDataViewer().
                        createOperationViewer(handback, mbean);
                if (comp == null) {
                    JTextArea textArea = new JTextArea(handback.toString());
                    textArea.setEditable(false);
                    textArea.setEnabled(true);
                    textArea.setRows(textArea.getLineCount());
                    JScrollPane scrollPane = new JScrollPane(textArea);
                    Dimension d = scrollPane.getPreferredSize();
                    if (d.getWidth() > 400 || d.getHeight() > 250) {
                        scrollPane.setPreferredSize(new Dimension(400, 250));
                    }
                    message = scrollPane;
                } else {
                    if (!(comp instanceof JScrollPane)) {
                        comp = new JScrollPane(comp);
                    }
                    Dimension d = comp.getPreferredSize();
                    if (d.getWidth() > 400 || d.getHeight() > 250) {
                        comp.setPreferredSize(new Dimension(400, 250));
                    }
                    message = comp;
                }
            }
            EventQueue.invokeLater(new ThreadDialog(
                    (Component) e.getSource(),
                    message,
                    Resources.getText("Operation return value"),
                    JOptionPane.INFORMATION_MESSAGE));
        }
        // Got notification
        else if (e.getType().equals(
                XMBeanNotifications.NOTIFICATION_RECEIVED_EVENT)) {
            DefaultMutableTreeNode emitter = (DefaultMutableTreeNode) handback;
            Long received = (Long) e.getUserData();
            updateReceivedNotifications(emitter, received.longValue(), true);
        }
    }

    /**
     * Action listener: handles actions in panel buttons
     */
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() instanceof JButton) {
            JButton button = (JButton) e.getSource();
            // Refresh button
            if (button == refreshButton) {
                mbeansTab.workerAdd(new Runnable() {
                    public void run() {
                        refreshAttributes();
                    }
                });
                return;
            }
            // Clear button
            if (button == clearButton) {
                clearCurrentNotifications();
                return;
            }
            // Subscribe button
            if (button == subscribeButton) {
                mbeansTab.workerAdd(new Runnable() {
                    public void run() {
                        try {
                            registerListener();
                        } catch (Throwable ex) {
                            ex = Utils.getActualException(ex);
                            EventQueue.invokeLater(new ThreadDialog(
                                    XSheet.this,
                                    ex.getMessage(),
                                    Resources.getText("Problem adding listener"),
                                    JOptionPane.ERROR_MESSAGE));
                        }
                    }
                });
                return;
            }
            // Unsubscribe button
            if (button == unsubscribeButton) {
                mbeansTab.workerAdd(new Runnable() {
                    public void run() {
                        try {
                            unregisterListener();
                        } catch (Throwable ex) {
                            ex = Utils.getActualException(ex);
                            EventQueue.invokeLater(new ThreadDialog(
                                    XSheet.this,
                                    ex.getMessage(),
                                    Resources.getText("Problem removing listener"),
                                    JOptionPane.ERROR_MESSAGE));
                        }
                    }
                });
                return;
            }
        }
    }
}