com.mirth.connect.client.ui.DashboardTreeTableModel.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.client.ui.DashboardTreeTableModel.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * 
 * http://www.mirthcorp.com
 * 
 * The software in this package is published under the terms of the MPL license a copy of which has
 * been included with this distribution in the LICENSE.txt file.
 */

package com.mirth.connect.client.ui;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.tree.TreePath;

import org.apache.commons.lang3.StringUtils;
import org.jdesktop.swingx.treetable.MutableTreeTableNode;
import org.jdesktop.swingx.treetable.TreeTableNode;

import com.mirth.connect.model.Channel;
import com.mirth.connect.model.ChannelGroup;
import com.mirth.connect.model.ChannelStatus;
import com.mirth.connect.model.DashboardStatus;
import com.mirth.connect.plugins.DashboardColumnPlugin;

public class DashboardTreeTableModel extends SortableTreeTableModel {

    private DashboardTableNodeFactory nodeFactory;
    private boolean groupModeEnabled = true;
    private MutableTreeTableNode groupRoot;
    private MutableTreeTableNode channelRoot;

    public DashboardTreeTableModel() {
        setSortChildNodes(true);
    }

    public void setNodeFactory(DashboardTableNodeFactory nodeFactory) {
        this.nodeFactory = nodeFactory;
    }

    public boolean isGroupModeEnabled() {
        return groupModeEnabled;
    }

    public MutableTreeTableNode getGroupRoot() {
        if (groupRoot == null) {
            initializeRoot();
        }
        return groupRoot;
    }

    public MutableTreeTableNode getChannelRoot() {
        if (channelRoot == null) {
            initializeRoot();
        }
        return channelRoot;
    }

    public void setGroupModeEnabled(boolean groupModeEnabled) {
        if (groupModeEnabled != this.groupModeEnabled) {
            this.groupModeEnabled = groupModeEnabled;
            if (getRoot() == null) {
                initializeRoot();
            } else {
                setRoot(groupModeEnabled ? groupRoot : channelRoot);
            }
        }
    }

    @Override
    public int getHierarchicalColumn() {
        return 1;
    }

    @Override
    public Class<?> getColumnClass(int column) {
        column -= getColumnOffset();

        // @formatter:off
        switch (column) {
        case 0:
            return CellData.class; // Status
        case 1:
            return String.class; // Name
        case 2:
            return Integer.class; // Revision
        case 3:
            return Calendar.class; // Last Deployed
        case 4:
            return Integer.class; // Received
        case 5:
            return Integer.class; // Filtered
        case 6:
            return Integer.class; // Queued
        case 7:
            return Integer.class; // Sent
        case 8:
            return Integer.class; // Errored
        case 9:
            return Integer.class; // Connection
        default:
            return String.class;
        }
        // @formatter:on
    }

    @Override
    public Object getChild(Object parent, int index) {
        if (!isValidTreeTableNode(parent)) {
            throw new IllegalArgumentException("parent must be a TreeTableNode managed by this model");
        }

        return ((TreeTableNode) parent).getChildAt(index);
    }

    @Override
    public int getChildCount(Object parent) {
        if (!isValidTreeTableNode(parent)) {
            throw new IllegalArgumentException("parent must be a TreeTableNode managed by this model");
        }

        return ((TreeTableNode) parent).getChildCount();
    }

    @Override
    public int getIndexOfChild(Object parent, Object child) {
        if (!isValidTreeTableNode(parent) || !isValidTreeTableNode(child)) {
            return -1;
        }

        return ((TreeTableNode) parent).getIndex((TreeTableNode) child);
    }

    @Override
    public Object getValueAt(Object node, int column) {
        if (!isValidTreeTableNode(node)) {
            throw new IllegalArgumentException("node must be a valid node managed by this model");
        }

        if (column < 0 || column >= getColumnCount()) {
            throw new IllegalArgumentException("column must be a valid index");
        }

        TreeTableNode ttn = (TreeTableNode) node;

        if (column >= ttn.getColumnCount()) {
            return null;
        }

        return ttn.getValueAt(column);
    }

    @Override
    public boolean isCellEditable(Object node, int column) {
        if (!isValidTreeTableNode(node)) {
            throw new IllegalArgumentException("node must be a valid node managed by this model");
        }

        if (column < 0 || column >= getColumnCount()) {
            throw new IllegalArgumentException("column must be a valid index");
        }

        TreeTableNode ttn = (TreeTableNode) node;

        if (column >= ttn.getColumnCount()) {
            return false;
        }

        return ttn.isEditable(column);
    }

    @Override
    public boolean isLeaf(Object node) {
        if (!isValidTreeTableNode(node)) {
            throw new IllegalArgumentException("node must be a TreeTableNode managed by this model");
        }

        return ((TreeTableNode) node).isLeaf();
    }

    @Override
    public void setValueAt(Object value, Object node, int column) {
        if (!isValidTreeTableNode(node)) {
            throw new IllegalArgumentException("node must be a valid node managed by this model");
        }

        if (column < 0 || column >= getColumnCount()) {
            throw new IllegalArgumentException("column must be a valid index");
        }

        TreeTableNode ttn = (TreeTableNode) node;

        if (column < ttn.getColumnCount()) {
            ttn.setValueAt(value, column);

            modelSupport.firePathChanged(new TreePath(getPathToRoot(ttn)));
        }
    }

    @Override
    public TreeTableNode[] getPathToRoot(TreeTableNode aNode) {
        List<TreeTableNode> path = new ArrayList<TreeTableNode>();
        TreeTableNode node = aNode;

        while (node != groupRoot && node != channelRoot) {
            path.add(0, node);

            node = node.getParent();
        }

        if (node == groupRoot || node == channelRoot) {
            path.add(0, node);
        }

        return path.toArray(new TreeTableNode[0]);
    }

    private boolean isValidTreeTableNode(Object node) {
        boolean result = false;

        if (node instanceof TreeTableNode) {
            TreeTableNode ttn = (TreeTableNode) node;

            while (!result && ttn != null) {
                result = ttn == groupRoot || ttn == channelRoot;

                ttn = ttn.getParent();
            }
        }

        return result;
    }

    public void setShowLifetimeStats(boolean showLifetimeStats) {
        setShowLifetimeStats(showLifetimeStats, groupRoot);
        setShowLifetimeStats(showLifetimeStats, channelRoot);
    }

    private void setShowLifetimeStats(boolean showLifetimeStats, MutableTreeTableNode parent) {
        if (parent == groupRoot) {
            for (Enumeration<? extends MutableTreeTableNode> groupNodes = groupRoot.children(); groupNodes
                    .hasMoreElements();) {
                AbstractDashboardTableNode groupNode = (AbstractDashboardTableNode) groupNodes.nextElement();
                setShowLifetimeStats(showLifetimeStats, groupNode);
                groupNode.setShowLifetimeStats(showLifetimeStats);
            }
        } else {
            for (int i = 0; i < parent.getChildCount(); i++) {
                AbstractDashboardTableNode node = (AbstractDashboardTableNode) parent.getChildAt(i);
                node.setShowLifetimeStats(showLifetimeStats);
                setShowLifetimeStats(showLifetimeStats, node);
            }
        }
    }

    private void initializeRoot() {
        if (getRoot() == null) {
            channelRoot = getNewRootNode();
            groupRoot = getNewRootNode();
            setRoot(groupModeEnabled ? groupRoot : channelRoot);
            addDefaultGroupNode();
        }
    }

    private AbstractDashboardTableNode addDefaultGroupNode() {
        AbstractDashboardTableNode defaultGroupNode = nodeFactory
                .createNode(new ChannelGroupStatus(ChannelGroup.getDefaultGroup(), new ArrayList<ChannelStatus>()));
        insertNodeInto(defaultGroupNode, groupRoot);
        return defaultGroupNode;
    }

    private MutableTreeTableNode getNewRootNode() {
        return new AbstractSortableTreeTableNode() {
            @Override
            public Object getValueAt(int arg0) {
                return null;
            }

            @Override
            public int getColumnCount() {
                return 0;
            }
        };
    }

    public void setStatuses(List<DashboardStatus> intermediateStatuses) {
        MutableTreeTableNode root = (MutableTreeTableNode) getRoot();

        if (root == null) {
            initializeRoot();
            add(intermediateStatuses, groupRoot);
            add(intermediateStatuses, channelRoot);
        } else {
            updateChannelNodes(new ArrayList<DashboardStatus>(intermediateStatuses), groupRoot);
            updateChannelNodes(new ArrayList<DashboardStatus>(intermediateStatuses), channelRoot);
        }
    }

    public void setGroupStatuses(Collection<ChannelGroupStatus> channelGroupStatuses) {
        Map<String, ChannelGroupStatus> groupStatusMap = new HashMap<String, ChannelGroupStatus>();
        for (ChannelGroupStatus groupStatus : channelGroupStatuses) {
            groupStatusMap.put(groupStatus.getGroup().getId(), groupStatus);
        }

        List<AbstractDashboardTableNode> tableGroupNodes = new ArrayList<AbstractDashboardTableNode>();
        AbstractDashboardTableNode defaultGroupNode = null;
        for (Enumeration<? extends MutableTreeTableNode> groupNodes = groupRoot.children(); groupNodes
                .hasMoreElements();) {
            AbstractDashboardTableNode groupNode = (AbstractDashboardTableNode) groupNodes.nextElement();
            if (StringUtils.equals(groupNode.getGroupStatus().getGroup().getId(), ChannelGroup.DEFAULT_ID)) {
                defaultGroupNode = groupNode;
            }
            tableGroupNodes.add(groupNode);
        }

        List<AbstractDashboardTableNode> groupNodesToRemove = new ArrayList<AbstractDashboardTableNode>();

        for (AbstractDashboardTableNode groupNode : tableGroupNodes) {
            ChannelGroup group = groupNode.getGroupStatus().getGroup();

            ChannelGroupStatus matchingGroupStatus = groupStatusMap.remove(group.getId());

            if (matchingGroupStatus != null) {
                ChannelGroup matchingGroup = matchingGroupStatus.getGroup();

                Set<String> channelIds = new HashSet<String>();
                for (Channel channel : matchingGroup.getChannels()) {
                    channelIds.add(channel.getId());
                }

                for (Channel channel : group.getChannels()) {
                    if (!channelIds.remove(channel.getId())) {
                        // Remove from the current group
                        AbstractDashboardTableNode channelNode = groupNode.getChannelNode(channel.getId());
                        if (channelNode != null) {
                            removeNodeFromParent(channelNode);

                            if (defaultGroupNode == null) {
                                defaultGroupNode = addDefaultGroupNode();
                            }
                            insertNodeInto(channelNode, defaultGroupNode);
                        }
                    }
                }

                for (String channelId : channelIds) {
                    // Move channel into the current group
                    AbstractDashboardTableNode channelNode = findChannelNode(channelId);
                    if (channelNode != null) {
                        removeNodeFromParent(channelNode);
                        insertNodeInto(channelNode, groupNode);
                    }
                }

                groupNode.setGroupStatus(matchingGroupStatus);
            } else {
                for (Channel channel : group.getChannels()) {
                    // Remove from the current group
                    AbstractDashboardTableNode channelNode = groupNode.getChannelNode(channel.getId());
                    if (channelNode != null) {
                        removeNodeFromParent(channelNode);
                        if (defaultGroupNode == null) {
                            defaultGroupNode = addDefaultGroupNode();
                        }
                        insertNodeInto(channelNode, defaultGroupNode);
                    }
                }

                // Remove the current group
                groupNodesToRemove.add(groupNode);
            }
        }

        for (ChannelGroupStatus groupStatus : groupStatusMap.values()) {
            // Add a new group node
            AbstractDashboardTableNode groupNode = nodeFactory.createNode(groupStatus);
            insertNodeInto(groupNode, groupRoot);

            for (Channel channel : groupStatus.getGroup().getChannels()) {
                // Move channel into the group
                if (defaultGroupNode == null) {
                    defaultGroupNode = addDefaultGroupNode();
                }
                AbstractDashboardTableNode channelNode = defaultGroupNode.getChannelNode(channel.getId());
                if (channelNode != null) {
                    removeNodeFromParent(channelNode);
                    insertNodeInto(channelNode, groupNode);
                }
            }

            groupNode.updateGroupNode();
        }

        for (AbstractDashboardTableNode groupNode : groupNodesToRemove) {
            removeNodeFromParent(groupNode);
        }
    }

    private AbstractDashboardTableNode findChannelNode(String channelId) {
        for (Enumeration<? extends MutableTreeTableNode> groupNodes = groupRoot.children(); groupNodes
                .hasMoreElements();) {
            AbstractDashboardTableNode channelNode = ((AbstractDashboardTableNode) groupNodes.nextElement())
                    .getChannelNode(channelId);
            if (channelNode != null) {
                return channelNode;
            }
        }
        return null;
    }

    public void refresh() {
        MutableTreeTableNode root = (MutableTreeTableNode) getRoot();

        if (root != null) {
            modelSupport.firePathChanged(new TreePath(root));
        }
    }

    /**
     * Should be called at the end with the final list of dashboard statuses.
     * 
     * @param finishedStatuses
     */
    public void finishStatuses(List<DashboardStatus> finishedStatuses) {
        initializeRoot();
        removeLingeringChannelNodes(finishedStatuses, groupRoot);
        removeLingeringChannelNodes(finishedStatuses, channelRoot);
    }

    private int getColumnOffset() {
        int counter = 0;

        for (DashboardColumnPlugin plugin : LoadedExtensions.getInstance().getDashboardColumnPlugins().values()) {
            if (plugin.isDisplayFirst()) {
                counter++;
            }
        }
        return counter;
    }

    private void add(Collection<DashboardStatus> statuses, MutableTreeTableNode parent) {
        if (parent == groupRoot) {
            Map<String, AbstractDashboardTableNode> groupNodeMap = new HashMap<String, AbstractDashboardTableNode>();
            for (Enumeration<? extends MutableTreeTableNode> groupNodes = groupRoot.children(); groupNodes
                    .hasMoreElements();) {
                AbstractDashboardTableNode groupNode = (AbstractDashboardTableNode) groupNodes.nextElement();

                for (Channel channel : groupNode.getGroupStatus().getGroup().getChannels()) {
                    groupNodeMap.put(channel.getId(), groupNode);
                }
            }

            AbstractDashboardTableNode defaultGroupNode = null;
            for (Enumeration<? extends MutableTreeTableNode> groupNodes = groupRoot.children(); groupNodes
                    .hasMoreElements();) {
                AbstractDashboardTableNode groupNode = (AbstractDashboardTableNode) groupNodes.nextElement();
                if (StringUtils.equals(groupNode.getGroupStatus().getGroup().getId(), ChannelGroup.DEFAULT_ID)) {
                    defaultGroupNode = groupNode;
                    break;
                }
            }

            for (DashboardStatus status : statuses) {
                AbstractDashboardTableNode groupNode = groupNodeMap.get(status.getChannelId());
                if (groupNode != null) {
                    add(Collections.singleton(status), groupNode);
                    groupNode.updateGroupNode();
                } else {
                    if (defaultGroupNode == null) {
                        defaultGroupNode = addDefaultGroupNode();
                    }
                    add(Collections.singleton(status), defaultGroupNode);
                    defaultGroupNode.updateGroupNode();
                }
            }
        } else {
            int index = parent.getChildCount();

            // add each status as a new child node of parent
            for (DashboardStatus status : statuses) {
                MutableTreeTableNode newNode = nodeFactory.createNode(status.getChannelId(), status);
                insertNodeInto(newNode, parent, index++);

                // recursively add the children of this status entry as child nodes of the newly created node
                add(status.getChildStatuses(), newNode);
            }
        }
    }

    private void updateChannelNodes(List<DashboardStatus> statuses, MutableTreeTableNode root) {
        // index the status entries by status key so that they can be retrieved quickly as we traverse the tree
        Map<String, DashboardStatus> statusMap = getStatusMap(statuses);

        if (root == groupRoot) {
            for (Enumeration<? extends MutableTreeTableNode> groupNodes = groupRoot.children(); groupNodes
                    .hasMoreElements();) {
                AbstractDashboardTableNode groupNode = (AbstractDashboardTableNode) groupNodes.nextElement();
                updateChannelNodes(statusMap, statuses, groupNode);
                groupNode.updateGroupNode();
            }
        } else {
            updateChannelNodes(statusMap, statuses, root);
        }

        // any remaining entries in the status list are new status entries that need to be added as nodes to the root node
        add(statuses, root);
    }

    private void updateChannelNodes(Map<String, DashboardStatus> statusMap, List<DashboardStatus> statuses,
            MutableTreeTableNode parent) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            AbstractDashboardTableNode node = (AbstractDashboardTableNode) parent.getChildAt(i);

            if (statusMap.containsKey(node.getDashboardStatus().getKey())) {
                // Remove channels that do exist from the list of statuses so they will not be added again later.
                DashboardStatus status = statusMap.get(node.getDashboardStatus().getKey());
                statuses.remove(status);
                // Update the channel status
                node.setDashboardStatus(status);
                modelSupport.firePathChanged(new TreePath(getPathToRoot(node)));

                // Update the connector level statuses
                updateConnector(statusMap, node);
            }
        }
    }

    private void removeLingeringChannelNodes(List<DashboardStatus> statuses, MutableTreeTableNode root) {
        // index the status entries by status key so that they can be retrieved quickly as we traverse the tree
        Map<String, DashboardStatus> statusMap = getStatusMap(statuses);

        if (root == groupRoot) {
            for (Enumeration<? extends MutableTreeTableNode> groupNodes = groupRoot.children(); groupNodes
                    .hasMoreElements();) {
                AbstractDashboardTableNode groupNode = (AbstractDashboardTableNode) groupNodes.nextElement();
                removeLingeringChannelNodes(statusMap, statuses, groupNode);
                groupNode.updateGroupNode();
            }
        } else {
            removeLingeringChannelNodes(statusMap, statuses, root);
        }
    }

    private void removeLingeringChannelNodes(Map<String, DashboardStatus> statusMap, List<DashboardStatus> statuses,
            MutableTreeTableNode root) {
        for (int i = 0; i < root.getChildCount(); i++) {
            AbstractDashboardTableNode node = (AbstractDashboardTableNode) root.getChildAt(i);

            if (!statusMap.containsKey(node.getDashboardStatus().getKey())) {
                // Remove channels that no longer exist
                removeNodeFromParent(node);
                i--;
            }
        }
    }

    private void updateConnector(Map<String, DashboardStatus> statusMap, AbstractDashboardTableNode channelNode) {
        Map<String, AbstractDashboardTableNode> nodeMap = new HashMap<String, AbstractDashboardTableNode>();

        DashboardStatus channelStatus = statusMap.get(channelNode.getDashboardStatus().getKey());

        // Remove connectors that no longer exist
        for (int i = 0; i < channelNode.getChildCount(); i++) {
            AbstractDashboardTableNode node = (AbstractDashboardTableNode) channelNode.getChildAt(i);

            if (!statusMap.containsKey(node.getDashboardStatus().getKey())) {
                removeNodeFromParent(node);
                i--;
            } else {
                nodeMap.put(node.getDashboardStatus().getKey(), node);
            }
        }

        // At this point, the only connectors remaining in the table should have an update.
        // Iterate across all the new statuses
        for (int i = 0; i < channelStatus.getChildStatuses().size(); i++) {
            // The new connector status at the current pointer
            DashboardStatus newConnectorStatus = channelStatus.getChildStatuses().get(i);
            // The node containing the old status
            AbstractDashboardTableNode oldConnectorNode = null;

            if (i < channelNode.getChildCount()) {
                oldConnectorNode = (AbstractDashboardTableNode) channelNode.getChildAt(i);
            }

            // If the new connector key is equal to the old one, then update the node's status
            if (oldConnectorNode != null
                    && newConnectorStatus.getKey().equals(oldConnectorNode.getDashboardStatus().getKey())) {
                oldConnectorNode.setDashboardStatus(newConnectorStatus);
                modelSupport.firePathChanged(new TreePath(getPathToRoot(oldConnectorNode)));
            } else {
                AbstractDashboardTableNode node;

                if (nodeMap.containsKey(newConnectorStatus.getKey())) {
                    // If the key is already in the table, remove it because it is out of order.
                    node = nodeMap.get(newConnectorStatus.getKey());
                    removeNodeFromParent(node);

                    // Update the node's status before it gets readded.
                    node.setDashboardStatus(newConnectorStatus);
                } else {
                    // If the key is not in the table yet, create a node for it.
                    node = nodeFactory.createNode(newConnectorStatus.getChannelId(), newConnectorStatus);
                }

                // Insert the node
                insertNodeInto(node, channelNode, i);
            }
        }
    }

    private Map<String, DashboardStatus> getStatusMap(List<DashboardStatus> statuses) {
        Map<String, DashboardStatus> statusMap = new LinkedHashMap<String, DashboardStatus>();

        for (DashboardStatus status : statuses) {
            statusMap.put(status.getKey(), status);
            for (DashboardStatus child : status.getChildStatuses()) {
                statusMap.put(child.getKey(), status);
            }
        }

        return statusMap;
    }
}