Java tutorial
/* * 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.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.prefs.Preferences; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListSelectionModel; import javax.swing.DropMode; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import net.miginfocom.swing.MigLayout; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SerializationException; import org.apache.commons.lang3.SerializationUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jdesktop.swingx.JXTaskPane; import org.jdesktop.swingx.decorator.ColorHighlighter; import org.jdesktop.swingx.decorator.ComponentAdapter; import org.jdesktop.swingx.decorator.HighlightPredicate; import org.jdesktop.swingx.decorator.Highlighter; import org.jdesktop.swingx.decorator.HighlighterFactory; import org.jdesktop.swingx.treetable.MutableTreeTableNode; import com.mirth.connect.client.core.ClientException; import com.mirth.connect.client.core.TaskConstants; import com.mirth.connect.client.ui.ChannelFilter.ChannelFilterSaveTask; import com.mirth.connect.client.ui.Frame.ChannelTask; import com.mirth.connect.client.ui.Frame.ConflictOption; import com.mirth.connect.client.ui.codetemplate.CodeTemplateImportDialog; import com.mirth.connect.client.ui.components.ChannelTableTransferHandler; import com.mirth.connect.client.ui.components.IconButton; import com.mirth.connect.client.ui.components.IconToggleButton; import com.mirth.connect.client.ui.components.MirthTreeTable; import com.mirth.connect.client.ui.components.rsta.MirthRTextScrollPane; import com.mirth.connect.client.ui.dependencies.ChannelDependenciesWarningDialog; import com.mirth.connect.donkey.util.DonkeyElement; import com.mirth.connect.donkey.util.DonkeyElement.DonkeyElementException; import com.mirth.connect.model.Channel; import com.mirth.connect.model.ChannelDependency; import com.mirth.connect.model.ChannelGroup; import com.mirth.connect.model.ChannelHeader; import com.mirth.connect.model.ChannelStatus; import com.mirth.connect.model.ChannelSummary; import com.mirth.connect.model.CodeTemplate; import com.mirth.connect.model.CodeTemplateLibrary; import com.mirth.connect.model.CodeTemplateLibrarySaveResult; import com.mirth.connect.model.CodeTemplateLibrarySaveResult.CodeTemplateUpdateResult; import com.mirth.connect.model.DashboardStatus; import com.mirth.connect.model.InvalidChannel; import com.mirth.connect.model.converters.ObjectXMLSerializer; import com.mirth.connect.model.util.ImportConverter3_0_0; import com.mirth.connect.plugins.ChannelColumnPlugin; import com.mirth.connect.plugins.ChannelPanelPlugin; import com.mirth.connect.plugins.TaskPlugin; import com.mirth.connect.util.ChannelDependencyException; import com.mirth.connect.util.ChannelDependencyGraph; import com.mirth.connect.util.DirectedAcyclicGraphNode; public class ChannelPanel extends AbstractFramePanel { public static final String STATUS_COLUMN_NAME = "Status"; public static final String DATA_TYPE_COLUMN_NAME = "Data Type"; public static final String NAME_COLUMN_NAME = "Name"; public static final String ID_COLUMN_NAME = "Id"; public static final String LOCAL_CHANNEL_ID = "Local Id"; public static final String DESCRIPTION_COLUMN_NAME = "Description"; public static final String DEPLOYED_REVISION_DELTA_COLUMN_NAME = "Rev \u0394"; public static final String LAST_DEPLOYED_COLUMN_NAME = "Last Deployed"; public static final String LAST_MODIFIED_COLUMN_NAME = "Last Modified"; public static final int STATUS_COLUMN_NUMBER = 0; public static final int DATA_TYPE_COLUMN_NUMBER = 1; public static final int NAME_COLUMN_NUMBER = 2; public static final int ID_COLUMN_NUMBER = 3; public static final int LOCAL_CHANNEL_ID_COLUMN_NUMBER = 4; public static final int DESCRIPTION_COLUMN_NUMBER = 5; public static final int DEPLOYED_REVISION_DELTA_COLUMN_NUMBER = 6; public static final int LAST_DEPLOYED_COLUMN_NUMBER = 7; public static final int LAST_MODIFIED_COLUMN_NUMBER = 8; private final static String[] DEFAULT_COLUMNS = new String[] { STATUS_COLUMN_NAME, DATA_TYPE_COLUMN_NAME, NAME_COLUMN_NAME, ID_COLUMN_NAME, LOCAL_CHANNEL_ID, DESCRIPTION_COLUMN_NAME, DEPLOYED_REVISION_DELTA_COLUMN_NAME, LAST_DEPLOYED_COLUMN_NAME, LAST_MODIFIED_COLUMN_NAME }; private static final int TASK_CHANNEL_REFRESH = 0; private static final int TASK_CHANNEL_REDEPLOY_ALL = 1; private static final int TASK_CHANNEL_DEPLOY = 2; private static final int TASK_CHANNEL_EDIT_GLOBAL_SCRIPTS = 3; private static final int TASK_CHANNEL_EDIT_CODE_TEMPLATES = 4; private static final int TASK_CHANNEL_NEW_CHANNEL = 5; private static final int TASK_CHANNEL_IMPORT_CHANNEL = 6; private static final int TASK_CHANNEL_EXPORT_ALL_CHANNELS = 7; private static final int TASK_CHANNEL_EXPORT_CHANNEL = 8; private static final int TASK_CHANNEL_DELETE_CHANNEL = 9; private static final int TASK_CHANNEL_CLONE = 10; private static final int TASK_CHANNEL_EDIT = 11; private static final int TASK_CHANNEL_ENABLE = 12; private static final int TASK_CHANNEL_DISABLE = 13; private static final int TASK_CHANNEL_VIEW_MESSAGES = 14; private static final int TASK_GROUP_SAVE = 0; private static final int TASK_GROUP_ASSIGN_CHANNEL = 1; private static final int TASK_GROUP_NEW_GROUP = 2; private static final int TASK_GROUP_EDIT_DETAILS = 3; private static final int TASK_GROUP_IMPORT_GROUP = 4; private static final int TASK_GROUP_EXPORT_ALL_GROUPS = 5; private static final int TASK_GROUP_EXPORT_GROUP = 6; private static final int TASK_GROUP_DELETE_GROUP = 7; private Frame parent; private Map<String, ChannelStatus> channelStatuses = new LinkedHashMap<String, ChannelStatus>(); private Map<String, ChannelGroupStatus> groupStatuses = new LinkedHashMap<String, ChannelGroupStatus>(); private Set<ChannelDependency> channelDependencies = new HashSet<ChannelDependency>(); public ChannelPanel() { this.parent = PlatformUI.MIRTH_FRAME; initComponents(); initLayout(); channelTasks = new JXTaskPane(); channelTasks.setTitle("Channel Tasks"); channelTasks.setName(TaskConstants.CHANNEL_KEY); channelTasks.setFocusable(false); channelPopupMenu = new JPopupMenu(); channelTable.setComponentPopupMenu(channelPopupMenu); parent.addTask(TaskConstants.CHANNEL_REFRESH, "Refresh", "Refresh the list of channels.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/arrow_refresh.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_REDEPLOY_ALL, "Redeploy All", "Undeploy all channels and deploy all currently enabled channels.", "A", new ImageIcon( com.mirth.connect.client.ui.Frame.class.getResource("images/arrow_rotate_clockwise.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_DEPLOY, "Deploy Channel", "Deploys the currently selected channel.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/arrow_redo.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_EDIT_GLOBAL_SCRIPTS, "Edit Global Scripts", "Edit scripts that are not channel specific.", "G", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/script_edit.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_EDIT_CODE_TEMPLATES, "Edit Code Templates", "Create and manage templates to be used in JavaScript throughout Mirth.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/page_edit.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_NEW_CHANNEL, "New Channel", "Create a new channel.", "N", new ImageIcon( com.mirth.connect.client.ui.Frame.class.getResource("images/application_form_add.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_IMPORT_CHANNEL, "Import Channel", "Import a channel from an XML file.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/report_go.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_EXPORT_ALL_CHANNELS, "Export All Channels", "Export all of the channels to XML files.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/report_disk.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_EXPORT_CHANNEL, "Export Channel", "Export the currently selected channel to an XML file.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/report_disk.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_DELETE_CHANNEL, "Delete Channel", "Delete the currently selected channel.", "L", new ImageIcon( com.mirth.connect.client.ui.Frame.class.getResource("images/application_form_delete.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_CLONE, "Clone Channel", "Clone the currently selected channel.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/page_copy.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_EDIT, "Edit Channel", "Edit the currently selected channel.", "I", new ImageIcon( com.mirth.connect.client.ui.Frame.class.getResource("images/application_form_edit.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_ENABLE, "Enable Channel", "Enable the currently selected channel.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/control_play_blue.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_DISABLE, "Disable Channel", "Disable the currently selected channel.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/control_stop_blue.png")), channelTasks, channelPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_VIEW_MESSAGES, "View Messages", "Show the messages for the currently selected channel.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/page_white_stack.png")), channelTasks, channelPopupMenu, this); parent.setNonFocusable(channelTasks); parent.taskPaneContainer.add(channelTasks, parent.taskPaneContainer.getComponentCount() - 1); groupTasks = new JXTaskPane(); groupTasks.setTitle("Group Tasks"); groupTasks.setName(TaskConstants.CHANNEL_GROUP_KEY); groupTasks.setFocusable(false); groupPopupMenu = new JPopupMenu(); parent.addTask(TaskConstants.CHANNEL_GROUP_SAVE, "Save Group Changes", "Save all changes made to channel groups.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/disk.png")), groupTasks, groupPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_GROUP_ASSIGN_CHANNEL, "Assign To Group", "Assign channel(s) to a group.", "A", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/report_go.png")), groupTasks, groupPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_GROUP_NEW_GROUP, "New Group", "Create a new channel group.", "N", new ImageIcon( com.mirth.connect.client.ui.Frame.class.getResource("images/application_form_add.png")), groupTasks, groupPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_GROUP_EDIT_DETAILS, "Edit Group Details", "Edit group name and description.", "E", new ImageIcon( com.mirth.connect.client.ui.Frame.class.getResource("images/application_form_edit.png")), groupTasks, groupPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_GROUP_EXPORT_ALL_GROUPS, "Export All Groups", "Export all of the channel groups to XML files.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/report_disk.png")), groupTasks, groupPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_GROUP_IMPORT_GROUP, "Import Group", "Import a channel group from an XML file.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/report_go.png")), groupTasks, groupPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_GROUP_EXPORT_GROUP, "Export Group", "Export the currently selected channel group to an XML file.", "", new ImageIcon(com.mirth.connect.client.ui.Frame.class.getResource("images/report_disk.png")), groupTasks, groupPopupMenu, this); parent.addTask(TaskConstants.CHANNEL_GROUP_DELETE_GROUP, "Delete Group", "Delete the currently selected channel group.", "L", new ImageIcon( com.mirth.connect.client.ui.Frame.class.getResource("images/application_form_delete.png")), groupTasks, groupPopupMenu, this); parent.setNonFocusable(groupTasks); parent.taskPaneContainer.add(groupTasks, parent.taskPaneContainer.getComponentCount() - 1); channelScrollPane.setComponentPopupMenu(channelPopupMenu); ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); if (Preferences.userNodeForPackage(Mirth.class).getBoolean("channelGroupViewEnabled", true)) { tableModeGroupsButton.setSelected(true); tableModeGroupsButton.setContentFilled(true); tableModeChannelsButton.setContentFilled(false); model.setGroupModeEnabled(true); } else { tableModeChannelsButton.setSelected(true); tableModeChannelsButton.setContentFilled(true); tableModeGroupsButton.setContentFilled(false); model.setGroupModeEnabled(false); } updateModel(new TableState(new ArrayList<String>(), null)); updateTasks(); } @Override public void switchPanel() { boolean groupViewEnabled = Preferences.userNodeForPackage(Mirth.class).getBoolean("channelGroupViewEnabled", true); switchTableMode(groupViewEnabled, false); if (groupViewEnabled) { tableModeGroupsButton.setSelected(true); tableModeGroupsButton.setContentFilled(true); tableModeChannelsButton.setContentFilled(false); } else { tableModeChannelsButton.setSelected(true); tableModeChannelsButton.setContentFilled(true); tableModeGroupsButton.setContentFilled(false); } List<JXTaskPane> taskPanes = new ArrayList<JXTaskPane>(); taskPanes.add(channelTasks); if (groupViewEnabled) { taskPanes.add(groupTasks); } for (TaskPlugin plugin : LoadedExtensions.getInstance().getTaskPlugins().values()) { JXTaskPane taskPane = plugin.getTaskPane(); if (taskPane != null) { taskPanes.add(taskPane); } } parent.setBold(parent.viewPane, 1); parent.setPanelName("Channels"); parent.setCurrentContentPage(ChannelPanel.this); parent.setFocus(taskPanes.toArray(new JXTaskPane[taskPanes.size()]), true, true); parent.setSaveEnabled(false); doRefreshChannels(); } @Override public boolean isSaveEnabled() { return groupTasks.getContentPane().getComponent(TASK_GROUP_SAVE).isVisible(); } @Override public void setSaveEnabled(boolean enabled) { setGroupTaskVisibility(TASK_GROUP_SAVE, enabled); } @Override public boolean changesHaveBeenMade() { return isSaveEnabled(); } @Override public void doContextSensitiveSave() { if (isSaveEnabled()) { doSaveGroups(); } } @Override public boolean confirmLeave() { return promptSave(false); } private boolean promptSave(boolean force) { int option; if (force) { option = JOptionPane.showConfirmDialog(parent, "You must save the channel group changes before continuing. Would you like to save now?"); } else { option = JOptionPane.showConfirmDialog(parent, "Would you like to save the channel groups?"); } if (option == JOptionPane.YES_OPTION) { return doSaveGroups(false); } else if (option == JOptionPane.CANCEL_OPTION || option == JOptionPane.CLOSED_OPTION || (option == JOptionPane.NO_OPTION && force)) { return false; } return true; } @Override protected Component addAction(Action action, Set<String> options) { Component taskComponent = channelTasks.add(action); channelPopupMenu.add(action); return taskComponent; } public Map<String, ChannelStatus> getCachedChannelStatuses() { return channelStatuses; } public Map<String, ChannelGroupStatus> getCachedGroupStatuses() { return groupStatuses; } public Set<ChannelDependency> getCachedChannelDependencies() { return channelDependencies; } public void doRefreshChannels() { doRefreshChannels(true); } public void doRefreshChannels(boolean queue) { if (isSaveEnabled() && !confirmLeave()) { return; } QueuingSwingWorkerTask<Void, Void> task = new QueuingSwingWorkerTask<Void, Void>("doRefreshChannels", "Loading channels...") { @Override public Void doInBackground() { retrieveChannels(); return null; } @Override public void done() { updateModel(getCurrentTableState()); updateTasks(); parent.setSaveEnabled(false); } }; new QueuingSwingWorker<Void, Void>(task, queue).executeDelegate(); } private void updateTasks() { int[] rows = channelTable.getSelectedModelRows(); ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); boolean filterEnabled = parent.getChannelTagInfo(false).isEnabled(); boolean saveEnabled = isSaveEnabled(); setAllTaskVisibility(false); setChannelTaskVisible(TASK_CHANNEL_REFRESH); setChannelTaskVisible(TASK_CHANNEL_REDEPLOY_ALL); setChannelTaskVisible(TASK_CHANNEL_EDIT_GLOBAL_SCRIPTS); setChannelTaskVisible(TASK_CHANNEL_EDIT_CODE_TEMPLATES); setChannelTaskVisible(TASK_CHANNEL_NEW_CHANNEL); setChannelTaskVisible(TASK_CHANNEL_IMPORT_CHANNEL); if (model.isGroupModeEnabled()) { if (!filterEnabled) { setGroupTaskVisible(TASK_GROUP_NEW_GROUP); if (!saveEnabled) { setGroupTaskVisible(TASK_GROUP_IMPORT_GROUP); } } if (!saveEnabled) { setGroupTaskVisible(TASK_GROUP_EXPORT_ALL_GROUPS); } } else { setChannelTaskVisible(TASK_CHANNEL_EXPORT_ALL_CHANNELS); } if (rows.length > 0) { boolean allGroups = true; boolean allChannels = true; boolean allEnabled = true; boolean allDisabled = true; boolean channelNodeFound = false; boolean includesDefaultGroup = false; for (int row : rows) { AbstractChannelTableNode node = (AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent(); if (node.isGroupNode()) { allChannels = false; for (Enumeration<? extends MutableTreeTableNode> channelNodes = node.children(); channelNodes .hasMoreElements();) { AbstractChannelTableNode channelNode = (AbstractChannelTableNode) channelNodes .nextElement(); if (channelNode.getChannelStatus().getChannel().isEnabled()) { allDisabled = false; } else { allEnabled = false; } channelNodeFound = true; } if (StringUtils.equals(node.getGroupStatus().getGroup().getId(), ChannelGroup.DEFAULT_ID)) { includesDefaultGroup = true; } } else { allGroups = false; if (node.getChannelStatus().getChannel().isEnabled()) { allDisabled = false; } else { allEnabled = false; } } } if (!allGroups || channelNodeFound) { if (!allDisabled) { setChannelTaskVisible(TASK_CHANNEL_DISABLE); } if (!allEnabled) { setChannelTaskVisible(TASK_CHANNEL_ENABLE); } } if (allGroups) { if (rows.length == 1 && !includesDefaultGroup && !filterEnabled) { setGroupTaskVisible(TASK_GROUP_EDIT_DETAILS); } if (channelNodeFound && !allDisabled) { setChannelTaskVisible(TASK_CHANNEL_DEPLOY); } if (!saveEnabled) { setGroupTaskVisible(TASK_GROUP_EXPORT_GROUP); } if (!includesDefaultGroup && !parent.getChannelTagInfo(false).isEnabled()) { setGroupTaskVisible(TASK_GROUP_DELETE_GROUP); } } else if (allChannels) { if (!allDisabled) { setChannelTaskVisible(TASK_CHANNEL_DEPLOY); } if (!filterEnabled && model.isGroupModeEnabled()) { setGroupTaskVisible(TASK_GROUP_ASSIGN_CHANNEL); } setChannelTaskVisible(TASK_CHANNEL_EXPORT_CHANNEL); setChannelTaskVisible(TASK_CHANNEL_DELETE_CHANNEL); if (rows.length == 1) { setChannelTaskVisible(TASK_CHANNEL_CLONE); setChannelTaskVisible(TASK_CHANNEL_EDIT); setChannelTaskVisible(TASK_CHANNEL_VIEW_MESSAGES); } } else { setChannelTaskVisible(TASK_CHANNEL_DEPLOY); } } } public void retrieveGroups() { try { updateChannelGroups(parent.mirthClient.getAllChannelGroups()); } catch (ClientException e) { parent.alertThrowable(parent, e, false); } } public void retrieveChannels() { try { updateChannelStatuses(parent.mirthClient.getChannelSummary(getChannelHeaders(), false)); updateChannelGroups(parent.mirthClient.getAllChannelGroups()); channelDependencies = parent.mirthClient.getChannelDependencies(); } catch (ClientException e) { parent.alertThrowable(parent, e); } } public void retrieveDependencies() { try { channelDependencies = parent.mirthClient.getChannelDependencies(); } catch (ClientException e) { parent.alertThrowable(parent, e); } } public Map<String, ChannelHeader> getChannelHeaders() { Map<String, ChannelHeader> channelHeaders = new HashMap<String, ChannelHeader>(); for (ChannelStatus channelStatus : channelStatuses.values()) { Channel channel = channelStatus.getChannel(); channelHeaders.put(channel.getId(), new ChannelHeader(channel.getRevision(), channelStatus.getDeployedDate(), channelStatus.isCodeTemplatesChanged())); } return channelHeaders; } public boolean doSaveGroups() { return doSaveGroups(true); } public boolean doSaveGroups(boolean asynchronous) { if (parent.getChannelTagInfo(false).isEnabled()) { return false; } Set<ChannelGroup> channelGroups = new HashSet<ChannelGroup>(); Set<String> removedChannelGroupIds = new HashSet<String>(groupStatuses.keySet()); removedChannelGroupIds.remove(ChannelGroup.DEFAULT_ID); MutableTreeTableNode root = (MutableTreeTableNode) channelTable.getTreeTableModel().getRoot(); if (root == null) { return false; } for (Enumeration<? extends MutableTreeTableNode> groupNodes = root.children(); groupNodes .hasMoreElements();) { ChannelGroup group = ((AbstractChannelTableNode) groupNodes.nextElement()).getGroupStatus().getGroup(); if (!StringUtils.equals(group.getId(), ChannelGroup.DEFAULT_ID)) { channelGroups.add(group); removedChannelGroupIds.remove(group.getId()); } if (StringUtils.isBlank(group.getName())) { parent.alertError(parent, "One or more groups have a blank name."); return false; } } if (asynchronous) { new UpdateSwingWorker(channelGroups, removedChannelGroupIds, false).execute(); } else { return attemptUpdate(channelGroups, removedChannelGroupIds, false); } return true; } private boolean attemptUpdate(Set<ChannelGroup> channelGroups, Set<String> removedChannelGroupIds, boolean override) { boolean result = false; boolean tryAgain = false; try { result = updateGroups(channelGroups, removedChannelGroupIds, override); if (result) { afterUpdate(); } else { if (!override) { if (parent.alertOption(parent, "One or more channel groups have been modified since you last refreshed.\nDo you want to overwrite the changes?")) { tryAgain = true; } } else { parent.alertError(parent, "Unable to save channel groups."); } } } catch (Exception e) { Throwable cause = e; if (cause instanceof ExecutionException) { cause = e.getCause(); } parent.alertThrowable(parent, cause, "Unable to save channel groups: " + cause.getMessage()); } if (tryAgain && !override) { return attemptUpdate(channelGroups, removedChannelGroupIds, true); } return result; } private boolean updateGroups(Set<ChannelGroup> channelGroups, Set<String> removedChannelGroupIds, boolean override) throws ClientException { return parent.mirthClient.updateChannelGroups(channelGroups, removedChannelGroupIds, override); } private void afterUpdate() { parent.setSaveEnabled(false); doRefreshChannels(); } public class UpdateSwingWorker extends SwingWorker<Boolean, Void> { private Set<ChannelGroup> channelGroups; private Set<String> removedChannelGroupIds; private boolean override; private String workingId; public UpdateSwingWorker(Set<ChannelGroup> channelGroups, Set<String> removedChannelGroupIds, boolean override) { this.channelGroups = channelGroups; this.removedChannelGroupIds = removedChannelGroupIds; this.override = override; workingId = parent.startWorking("Saving channel groups..."); } @Override protected Boolean doInBackground() throws Exception { return updateGroups(channelGroups, removedChannelGroupIds, override); } @Override protected void done() { boolean tryAgain = false; try { Boolean result = get(); if (result) { afterUpdate(); } else { if (!override) { if (parent.alertOption(parent, "One or more channel groups have been modified since you last refreshed.\nDo you want to overwrite the changes?")) { tryAgain = true; } } else { parent.alertError(parent, "Unable to save channel groups."); } } } catch (Exception e) { Throwable cause = e; if (cause instanceof ExecutionException) { cause = e.getCause(); } parent.alertThrowable(parent, cause, "Unable to save channel groups: " + cause.getMessage()); } finally { parent.stopWorking(workingId); if (tryAgain && !override) { new UpdateSwingWorker(channelGroups, removedChannelGroupIds, true).execute(); } } } } public void doRedeployAll() { if (!parent.alertOption(parent, "Are you sure you want to redeploy all channels?")) { return; } final String workingId = parent.startWorking("Deploying channels..."); parent.dashboardPanel.deselectRows(false); parent.doShowDashboard(); SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { @Override public Void doInBackground() { try { parent.mirthClient.redeployAllChannels(); } catch (ClientException e) { parent.alertThrowable(parent, e); } return null; } @Override public void done() { parent.stopWorking(workingId); parent.doRefreshStatuses(true); } }; worker.execute(); } public void doDeployChannel() { List<Channel> selectedChannels = getSelectedChannels(); if (selectedChannels.size() == 0) { parent.alertWarning(parent, "Channel no longer exists."); return; } // Only deploy enabled channels final Set<String> selectedEnabledChannelIds = new LinkedHashSet<String>(); boolean channelDisabled = false; for (Channel channel : selectedChannels) { if (channel.isEnabled()) { selectedEnabledChannelIds.add(channel.getId()); } else { channelDisabled = true; } } if (channelDisabled) { parent.alertWarning(parent, "Disabled channels will not be deployed."); } // If there are any channel dependencies, decide if we need to warn the user on deploy. try { ChannelDependencyGraph channelDependencyGraph = new ChannelDependencyGraph(channelDependencies); Set<String> deployedChannelIds = new HashSet<String>(); if (parent.status != null) { for (DashboardStatus dashboardStatus : parent.status) { deployedChannelIds.add(dashboardStatus.getChannelId()); } } // For each selected channel, add any dependent/dependency channels as necessary Set<String> channelIdsToDeploy = new HashSet<String>(); for (String channelId : selectedEnabledChannelIds) { addChannelToDeploySet(channelId, channelDependencyGraph, deployedChannelIds, channelIdsToDeploy); } // If additional channels were added to the set, we need to prompt the user if (!CollectionUtils.subtract(channelIdsToDeploy, selectedEnabledChannelIds).isEmpty()) { ChannelDependenciesWarningDialog dialog = new ChannelDependenciesWarningDialog(ChannelTask.DEPLOY, channelDependencies, selectedEnabledChannelIds, channelIdsToDeploy); if (dialog.getResult() == JOptionPane.OK_OPTION) { if (dialog.isIncludeOtherChannels()) { selectedEnabledChannelIds.addAll(channelIdsToDeploy); } } else { return; } } } catch (ChannelDependencyException e) { // Should never happen e.printStackTrace(); } parent.deployChannel(selectedEnabledChannelIds); } private void addChannelToDeploySet(String channelId, ChannelDependencyGraph channelDependencyGraph, Set<String> deployedChannelIds, Set<String> channelIdsToDeploy) { if (!channelIdsToDeploy.add(channelId)) { return; } DirectedAcyclicGraphNode<String> node = channelDependencyGraph.getNode(channelId); if (node != null) { for (String dependentChannelId : node.getDirectDependentElements()) { ChannelStatus channelStatus = channelStatuses.get(dependentChannelId); // Only add the dependent channel if it's enabled and currently deployed if (channelStatus != null && channelStatus.getChannel().isEnabled() && deployedChannelIds.contains(dependentChannelId)) { addChannelToDeploySet(dependentChannelId, channelDependencyGraph, deployedChannelIds, channelIdsToDeploy); } } for (String dependencyChannelId : node.getDirectDependencyElements()) { ChannelStatus channelStatus = channelStatuses.get(dependencyChannelId); // Only add the dependency channel it it's enabled if (channelStatus != null && channelStatus.getChannel().isEnabled()) { addChannelToDeploySet(dependencyChannelId, channelDependencyGraph, deployedChannelIds, channelIdsToDeploy); } } } } public void doEditGlobalScripts() { if (isSaveEnabled() && !confirmLeave()) { return; } parent.doEditGlobalScripts(); } public void doEditCodeTemplates() { if (isSaveEnabled() && !confirmLeave()) { return; } parent.doEditCodeTemplates(); } public void doNewGroup() { if (parent.getChannelTagInfo(false).isEnabled()) { return; } ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); MutableTreeTableNode root = (MutableTreeTableNode) model.getRoot(); if (root == null) { return; } GroupDetailsDialog dialog = new GroupDetailsDialog(true); if (dialog.wasSaved()) { AbstractChannelTableNode groupNode = model .addNewGroup(new ChannelGroup(dialog.getGroupName(), dialog.getGroupDescription())); parent.setSaveEnabled(true); final TreePath path = new TreePath(new Object[] { root, groupNode }); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { channelTable.getTreeSelectionModel().setSelectionPath(path); } }); } } private boolean checkGroupId(String id) { MutableTreeTableNode root = (MutableTreeTableNode) channelTable.getTreeTableModel().getRoot(); if (root == null) { return false; } for (Enumeration<? extends MutableTreeTableNode> groupNodes = root.children(); groupNodes .hasMoreElements();) { if (StringUtils.equals( ((AbstractChannelTableNode) groupNodes.nextElement()).getGroupStatus().getGroup().getId(), id)) { return false; } } return true; } private boolean checkGroupName(String name) { return checkGroupName(name, true); } private boolean checkGroupName(String name, boolean includeSelectedRow) { MutableTreeTableNode root = (MutableTreeTableNode) channelTable.getTreeTableModel().getRoot(); if (root == null) { return false; } for (Enumeration<? extends MutableTreeTableNode> groupNodes = root.children(); groupNodes .hasMoreElements();) { AbstractChannelTableNode groupNode = (AbstractChannelTableNode) groupNodes.nextElement(); if (!includeSelectedRow && channelTable.getSelectedRow() != -1) { AbstractChannelTableNode selectedNode = (AbstractChannelTableNode) channelTable .getPathForRow(channelTable.getSelectedRow()).getLastPathComponent(); if (selectedNode.isGroupNode() && selectedNode.getGroupStatus().getGroup().getId() .equals(groupNode.getGroupStatus().getGroup().getId())) { continue; } } if (StringUtils.equals(groupNode.getGroupStatus().getGroup().getName(), name)) { return false; } } return true; } public void doAssignChannelToGroup() { ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); int[] rows = channelTable.getSelectedModelRows(); if (model.isGroupModeEnabled() && rows.length > 0) { for (int row : rows) { if (((AbstractChannelTableNode) channelTable.getPathForRow(row).getLastPathComponent()) .isGroupNode()) { return; } } GroupAssignmentDialog dialog = new GroupAssignmentDialog(); if (dialog.wasSaved()) { AbstractChannelTableNode groupNode = null; for (Enumeration<? extends MutableTreeTableNode> groupNodes = ((MutableTreeTableNode) model .getRoot()).children(); groupNodes.hasMoreElements();) { AbstractChannelTableNode node = (AbstractChannelTableNode) groupNodes.nextElement(); if (StringUtils.equals(node.getGroupStatus().getGroup().getId(), dialog.getSelectedGroupId())) { groupNode = node; break; } } if (groupNode != null) { TableState tableState = getCurrentTableState(); tableState.getExpandedGroupIds().add(groupNode.getGroupStatus().getGroup().getId()); ListSelectionListener[] listeners = ((DefaultListSelectionModel) channelTable .getSelectionModel()).getListSelectionListeners(); for (ListSelectionListener listener : listeners) { channelTable.getSelectionModel().removeListSelectionListener(listener); } try { List<AbstractChannelTableNode> channelNodes = new ArrayList<AbstractChannelTableNode>(); for (int row : rows) { channelNodes.add((AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent()); } for (AbstractChannelTableNode channelNode : channelNodes) { model.addChannelToGroup(groupNode, channelNode.getChannelStatus().getChannel().getId()); } channelTable.expandPath(new TreePath( new Object[] { channelTable.getTreeTableModel().getRoot(), groupNode })); parent.setSaveEnabled(true); } finally { for (ListSelectionListener listener : listeners) { channelTable.getSelectionModel().addListSelectionListener(listener); } restoreTableState(tableState); } } } } } public void doEditGroupDetails() { if (parent.getChannelTagInfo(false).isEnabled()) { return; } ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); MutableTreeTableNode root = (MutableTreeTableNode) model.getRoot(); if (root == null) { return; } int[] rows = channelTable.getSelectedModelRows(); if (rows.length == 1) { AbstractChannelTableNode node = (AbstractChannelTableNode) channelTable.getPathForRow(rows[0]) .getLastPathComponent(); if (node.isGroupNode() && !StringUtils.equals(node.getGroupStatus().getGroup().getId(), ChannelGroup.DEFAULT_ID)) { GroupDetailsDialog dialog = new GroupDetailsDialog(false); if (dialog.wasSaved()) { channelTable.getTreeTableModel().setValueAt(new ChannelTableNameEntry(dialog.getGroupName()), node, NAME_COLUMN_NUMBER); channelTable.getTreeTableModel().setValueAt(dialog.getGroupDescription(), node, DESCRIPTION_COLUMN_NUMBER); parent.setSaveEnabled(true); } } } } public void doNewChannel() { if (isSaveEnabled() && !promptSave(true)) { return; } if (LoadedExtensions.getInstance().getSourceConnectors().size() == 0 || LoadedExtensions.getInstance().getDestinationConnectors().size() == 0) { parent.alertError(parent, "You must have at least one source connector and one destination connector installed."); return; } // The channel wizard will call createNewChannel() or create a channel // from a wizard. new ChannelWizard(); } public void createNewChannel() { if (isSaveEnabled() && !promptSave(true)) { return; } Channel channel = new Channel(); try { channel.setId(parent.mirthClient.getGuid()); } catch (ClientException e) { parent.alertThrowable(parent, e); } channel.setName(""); Set<String> selectedGroupIds = new HashSet<String>(); if (((ChannelTreeTableModel) channelTable.getTreeTableModel()).isGroupModeEnabled()) { for (int row : channelTable.getSelectedModelRows()) { TreePath path = channelTable.getPathForRow(row); if (path != null) { AbstractChannelTableNode node = (AbstractChannelTableNode) path.getLastPathComponent(); if (node.isGroupNode()) { selectedGroupIds.add(node.getGroupStatus().getGroup().getId()); } else if (node.getParent() instanceof AbstractChannelTableNode) { node = (AbstractChannelTableNode) node.getParent(); if (node.isGroupNode()) { selectedGroupIds.add(node.getGroupStatus().getGroup().getId()); } } } } } parent.setupChannel(channel, selectedGroupIds.size() == 1 ? selectedGroupIds.iterator().next() : null); } public void addChannelToGroup(String channelId, String groupId) { Set<ChannelGroup> channelGroups = new HashSet<ChannelGroup>(); for (ChannelGroupStatus groupStatus : groupStatuses.values()) { ChannelGroup group = groupStatus.getGroup(); if (!group.getId().equals(ChannelGroup.DEFAULT_ID)) { if (group.getId().equals(groupId)) { group.getChannels().add(new Channel(channelId)); } channelGroups.add(group); } } new UpdateSwingWorker(channelGroups, new HashSet<String>(), false).execute(); } public void doImportGroup() { if (isSaveEnabled() && !promptSave(true)) { return; } String content = parent.browseForFileString("XML"); if (content != null) { importGroup(content, true); } } public void importGroup(String content, boolean showAlerts) { if (showAlerts && !parent.promptObjectMigration(content, "group")) { return; } ChannelGroup importGroup = null; try { importGroup = ObjectXMLSerializer.getInstance().deserialize(content, ChannelGroup.class); } catch (Exception e) { if (showAlerts) { parent.alertThrowable(parent, e, "Invalid channel group file:\n" + e.getMessage()); } return; } importGroup(importGroup, showAlerts); } public void importGroup(ChannelGroup importGroup, boolean showAlerts) { importGroup(importGroup, showAlerts, false); } public void importGroup(ChannelGroup importGroup, boolean showAlerts, boolean synchronous) { // First consolidate and import code template libraries Map<String, CodeTemplateLibrary> codeTemplateLibraryMap = new LinkedHashMap<String, CodeTemplateLibrary>(); Set<String> codeTemplateIds = new HashSet<String>(); for (Channel channel : importGroup.getChannels()) { if (channel.getCodeTemplateLibraries() != null) { for (CodeTemplateLibrary library : channel.getCodeTemplateLibraries()) { CodeTemplateLibrary matchingLibrary = codeTemplateLibraryMap.get(library.getId()); if (matchingLibrary != null) { for (CodeTemplate codeTemplate : library.getCodeTemplates()) { if (codeTemplateIds.add(codeTemplate.getId())) { matchingLibrary.getCodeTemplates().add(codeTemplate); } } } else { matchingLibrary = library; codeTemplateLibraryMap.put(matchingLibrary.getId(), matchingLibrary); List<CodeTemplate> codeTemplates = new ArrayList<CodeTemplate>(); for (CodeTemplate codeTemplate : matchingLibrary.getCodeTemplates()) { if (codeTemplateIds.add(codeTemplate.getId())) { codeTemplates.add(codeTemplate); } } matchingLibrary.setCodeTemplates(codeTemplates); } // Combine the library enabled / disabled channel IDs matchingLibrary.getEnabledChannelIds().addAll(library.getEnabledChannelIds()); matchingLibrary.getEnabledChannelIds().add(channel.getId()); matchingLibrary.getDisabledChannelIds().addAll(library.getDisabledChannelIds()); matchingLibrary.getDisabledChannelIds().removeAll(matchingLibrary.getEnabledChannelIds()); } channel.getCodeTemplateLibraries().clear(); } } List<CodeTemplateLibrary> codeTemplateLibraries = new ArrayList<CodeTemplateLibrary>( codeTemplateLibraryMap.values()); parent.removeInvalidItems(codeTemplateLibraries, CodeTemplateLibrary.class); if (CollectionUtils.isNotEmpty(codeTemplateLibraries)) { boolean importLibraries; String importChannelCodeTemplateLibraries = Preferences.userNodeForPackage(Mirth.class) .get("importChannelCodeTemplateLibraries", null); if (importChannelCodeTemplateLibraries == null) { JCheckBox alwaysChooseCheckBox = new JCheckBox( "Always choose this option by default in the future (may be changed in the Administrator settings)"); Object[] params = new Object[] { "Group \"" + importGroup.getName() + "\" has code template libraries included with it. Would you like to import them?", alwaysChooseCheckBox }; int result = JOptionPane.showConfirmDialog(this, params, "Select an Option", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.YES_OPTION || result == JOptionPane.NO_OPTION) { importLibraries = result == JOptionPane.YES_OPTION; if (alwaysChooseCheckBox.isSelected()) { Preferences.userNodeForPackage(Mirth.class).putBoolean("importChannelCodeTemplateLibraries", importLibraries); } } else { return; } } else { importLibraries = Boolean.parseBoolean(importChannelCodeTemplateLibraries); } if (importLibraries) { CodeTemplateImportDialog dialog = new CodeTemplateImportDialog(parent, codeTemplateLibraries, false, true); if (dialog.wasSaved()) { CodeTemplateLibrarySaveResult updateSummary = parent.codeTemplatePanel.attemptUpdate( dialog.getUpdatedLibraries(), new HashMap<String, CodeTemplateLibrary>(), dialog.getUpdatedCodeTemplates(), new HashMap<String, CodeTemplate>(), true, null, null); if (updateSummary == null || updateSummary.isOverrideNeeded() || !updateSummary.isLibrariesSuccess()) { return; } else { for (CodeTemplateUpdateResult result : updateSummary.getCodeTemplateResults().values()) { if (!result.isSuccess()) { return; } } } parent.codeTemplatePanel.doRefreshCodeTemplates(); } } } List<Channel> successfulChannels = new ArrayList<Channel>(); for (Channel channel : importGroup.getChannels()) { Channel importChannel = importChannel(channel, false, false); if (importChannel != null) { successfulChannels.add(importChannel); } } if (!StringUtils.equals(importGroup.getId(), ChannelGroup.DEFAULT_ID)) { ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); AbstractChannelTableNode importGroupNode = null; String groupName = importGroup.getName(); String tempId; try { tempId = parent.mirthClient.getGuid(); } catch (ClientException e) { tempId = UUID.randomUUID().toString(); } // Check to see that the channel name doesn't already exist. if (!checkGroupName(groupName)) { if (!parent.alertOption(parent, "Would you like to overwrite the existing group? Choose 'No' to create a new group.")) { importGroup.setRevision(0); do { groupName = JOptionPane.showInputDialog(this, "Please enter a new name for the group.", groupName); if (groupName == null) { return; } } while (!checkGroupName(groupName)); importGroup.setId(tempId); importGroup.setName(groupName); } else { MutableTreeTableNode root = (MutableTreeTableNode) model.getRoot(); for (Enumeration<? extends MutableTreeTableNode> groupNodes = root.children(); groupNodes .hasMoreElements();) { AbstractChannelTableNode groupNode = (AbstractChannelTableNode) groupNodes.nextElement(); if (StringUtils.equals(groupNode.getGroupStatus().getGroup().getName(), groupName)) { importGroupNode = groupNode; } } } } else { // Start the revision number over for a new channel group importGroup.setRevision(0); // If the channel name didn't already exist, make sure // the id doesn't exist either. if (!checkGroupId(importGroup.getId())) { importGroup.setId(tempId); } } Set<ChannelGroup> channelGroups = new HashSet<ChannelGroup>(); Set<String> removedChannelGroupIds = new HashSet<String>(groupStatuses.keySet()); removedChannelGroupIds.remove(ChannelGroup.DEFAULT_ID); MutableTreeTableNode root = (MutableTreeTableNode) channelTable.getTreeTableModel().getRoot(); if (root == null) { return; } for (Enumeration<? extends MutableTreeTableNode> groupNodes = root.children(); groupNodes .hasMoreElements();) { ChannelGroup group = ((AbstractChannelTableNode) groupNodes.nextElement()).getGroupStatus() .getGroup(); if (!StringUtils.equals(group.getId(), ChannelGroup.DEFAULT_ID)) { // If the current group is the one we're overwriting, merge the channels if (importGroupNode != null && StringUtils.equals(group.getId(), importGroupNode.getGroupStatus().getGroup().getId())) { group = importGroup; group.setRevision(importGroupNode.getGroupStatus().getGroup().getRevision()); Set<String> channelIds = new HashSet<String>(); for (Channel channel : group.getChannels()) { channelIds.add(channel.getId()); } // Add the imported channels for (Channel channel : successfulChannels) { channelIds.add(channel.getId()); } List<Channel> channels = new ArrayList<Channel>(); for (String channelId : channelIds) { channels.add(new Channel(channelId)); } group.setChannels(channels); } channelGroups.add(group); removedChannelGroupIds.remove(group.getId()); } } if (importGroupNode == null) { List<Channel> channels = new ArrayList<Channel>(); for (Channel channel : successfulChannels) { channels.add(new Channel(channel.getId())); } importGroup.setChannels(channels); channelGroups.add(importGroup); removedChannelGroupIds.remove(importGroup.getId()); } Set<String> channelIds = new HashSet<String>(); for (Channel channel : importGroup.getChannels()) { channelIds.add(channel.getId()); } for (ChannelGroup group : channelGroups) { if (group != importGroup) { for (Iterator<Channel> channels = group.getChannels().iterator(); channels.hasNext();) { if (!channelIds.add(channels.next().getId())) { channels.remove(); } } } } attemptUpdate(channelGroups, removedChannelGroupIds, false); } if (synchronous) { retrieveChannels(); updateModel(getCurrentTableState()); updateTasks(); parent.setSaveEnabled(false); } else { doRefreshChannels(); } } public void doImportChannel() { if (isSaveEnabled() && !promptSave(true)) { return; } String content = parent.browseForFileString("XML"); if (content != null) { importChannel(content, true); } } public void importChannel(String content, boolean showAlerts) { if (showAlerts && !parent.promptObjectMigration(content, "channel")) { return; } Channel importChannel = null; try { importChannel = ObjectXMLSerializer.getInstance().deserialize(content, Channel.class); } catch (Exception e) { if (showAlerts) { parent.alertThrowable(parent, e, "Invalid channel file:\n" + e.getMessage()); } return; } importChannel(importChannel, showAlerts); } public Channel importChannel(Channel importChannel, boolean showAlerts) { return importChannel(importChannel, showAlerts, true); } public Channel importChannel(Channel importChannel, boolean showAlerts, boolean refreshStatuses) { boolean overwrite = false; try { String channelName = importChannel.getName(); String tempId = parent.mirthClient.getGuid(); // Check to see that the channel name doesn't already exist. if (!parent.checkChannelName(channelName, tempId)) { if (!parent.alertOption(parent, "Would you like to overwrite the existing channel? Choose 'No' to create a new channel.")) { importChannel.setRevision(0); do { channelName = JOptionPane.showInputDialog(this, "Please enter a new name for the channel.", channelName); if (channelName == null) { return null; } } while (!parent.checkChannelName(channelName, tempId)); importChannel.setName(channelName); setIdAndUpdateLibraries(importChannel, tempId); } else { overwrite = true; for (ChannelStatus channelStatus : channelStatuses.values()) { Channel channel = channelStatus.getChannel(); if (channel.getName().equalsIgnoreCase(channelName)) { // If overwriting, use the old revision number and id importChannel.setRevision(channel.getRevision()); setIdAndUpdateLibraries(importChannel, channel.getId()); } } } } else { // Start the revision number over for a new channel importChannel.setRevision(0); // If the channel name didn't already exist, make sure // the id doesn't exist either. if (!checkChannelId(importChannel.getId())) { setIdAndUpdateLibraries(importChannel, tempId); } } channelStatuses.put(importChannel.getId(), new ChannelStatus(importChannel)); parent.updateChannelTags(false); } catch (ClientException e) { parent.alertThrowable(parent, e); } // Import code templates / libraries if applicable parent.removeInvalidItems(importChannel.getCodeTemplateLibraries(), CodeTemplateLibrary.class); if (!(importChannel instanceof InvalidChannel) && !importChannel.getCodeTemplateLibraries().isEmpty()) { boolean importLibraries; String importChannelCodeTemplateLibraries = Preferences.userNodeForPackage(Mirth.class) .get("importChannelCodeTemplateLibraries", null); if (importChannelCodeTemplateLibraries == null) { JCheckBox alwaysChooseCheckBox = new JCheckBox( "Always choose this option by default in the future (may be changed in the Administrator settings)"); Object[] params = new Object[] { "Channel \"" + importChannel.getName() + "\" has code template libraries included with it. Would you like to import them?", alwaysChooseCheckBox }; int result = JOptionPane.showConfirmDialog(this, params, "Select an Option", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.YES_OPTION || result == JOptionPane.NO_OPTION) { importLibraries = result == JOptionPane.YES_OPTION; if (alwaysChooseCheckBox.isSelected()) { Preferences.userNodeForPackage(Mirth.class).putBoolean("importChannelCodeTemplateLibraries", importLibraries); } } else { return null; } } else { importLibraries = Boolean.parseBoolean(importChannelCodeTemplateLibraries); } if (importLibraries) { CodeTemplateImportDialog dialog = new CodeTemplateImportDialog(parent, importChannel.getCodeTemplateLibraries(), false, true); if (dialog.wasSaved()) { CodeTemplateLibrarySaveResult updateSummary = parent.codeTemplatePanel.attemptUpdate( dialog.getUpdatedLibraries(), new HashMap<String, CodeTemplateLibrary>(), dialog.getUpdatedCodeTemplates(), new HashMap<String, CodeTemplate>(), true, null, null); if (updateSummary == null || updateSummary.isOverrideNeeded() || !updateSummary.isLibrariesSuccess()) { return null; } else { for (CodeTemplateUpdateResult result : updateSummary.getCodeTemplateResults().values()) { if (!result.isSuccess()) { return null; } } } parent.codeTemplatePanel.doRefreshCodeTemplates(); } } importChannel.getCodeTemplateLibraries().clear(); } if (CollectionUtils.isNotEmpty(importChannel.getDependentIds()) || CollectionUtils.isNotEmpty(importChannel.getDependencyIds())) { Set<ChannelDependency> channelDependencies = new HashSet<ChannelDependency>( getCachedChannelDependencies()); if (CollectionUtils.isNotEmpty(importChannel.getDependentIds())) { for (String dependentId : importChannel.getDependentIds()) { if (StringUtils.isNotBlank(dependentId) && !StringUtils.equals(dependentId, importChannel.getId())) { channelDependencies.add(new ChannelDependency(dependentId, importChannel.getId())); } } } if (CollectionUtils.isNotEmpty(importChannel.getDependencyIds())) { for (String dependencyId : importChannel.getDependencyIds()) { if (StringUtils.isNotBlank(dependencyId) && !StringUtils.equals(dependencyId, importChannel.getId())) { channelDependencies.add(new ChannelDependency(importChannel.getId(), dependencyId)); } } } if (!channelDependencies.equals(getCachedChannelDependencies())) { try { parent.mirthClient.setChannelDependencies(channelDependencies); } catch (ClientException e) { parent.alertThrowable(parent, e, "Unable to save channel dependencies."); } } importChannel.clearDependencies(); } // Update resource names parent.updateResourceNames(importChannel); /* * Update the channel if we're overwriting an imported channel, if we're not showing alerts * (dragging/dropping multiple channels), or if we're working with an invalid channel. */ if (overwrite || !showAlerts || importChannel instanceof InvalidChannel) { try { parent.updateChannel(importChannel, overwrite); if (importChannel instanceof InvalidChannel && showAlerts) { InvalidChannel invalidChannel = (InvalidChannel) importChannel; Throwable cause = invalidChannel.getCause(); parent.alertThrowable(parent, cause, "Channel \"" + importChannel.getName() + "\" is invalid. " + getMissingExtensions(invalidChannel) + " Original cause:\n" + cause.getMessage()); } } catch (Exception e) { channelStatuses.remove(importChannel.getId()); parent.updateChannelTags(false); parent.alertThrowable(parent, e); return null; } finally { if (refreshStatuses) { doRefreshChannels(); } } } if (showAlerts) { final Channel importChannelFinal = importChannel; final boolean overwriteFinal = overwrite; /* * MIRTH-2048 - This is a hack to fix the memory access error that only occurs on OS X. * The block of code that edits the channel needs to be invoked later so that the screen * does not change before the drag/drop action of a channel finishes. */ SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { parent.editChannel(importChannelFinal); parent.setSaveEnabled(!overwriteFinal); } catch (Exception e) { channelStatuses.remove(importChannelFinal.getId()); parent.updateChannelTags(false); parent.alertError(parent, "Channel had an unknown problem. Channel import aborted."); parent.channelEditPanel = new ChannelSetup(); parent.doShowChannel(); } } }); } return importChannel; } private void setIdAndUpdateLibraries(Channel channel, String newChannelId) { for (CodeTemplateLibrary library : channel.getCodeTemplateLibraries()) { library.getEnabledChannelIds().remove(channel.getId()); library.getEnabledChannelIds().add(newChannelId); } channel.setId(newChannelId); } public void doExportAllChannels() { if (isSaveEnabled() && !promptSave(true)) { return; } if (!channelStatuses.isEmpty()) { List<Channel> selectedChannels = new ArrayList<Channel>(); for (ChannelStatus channelStatus : channelStatuses.values()) { selectedChannels.add(channelStatus.getChannel()); } exportChannels(selectedChannels); } } public boolean doExportChannel() { if (isSaveEnabled() && !promptSave(true)) { return false; } if (isGroupSelected()) { JOptionPane.showMessageDialog(parent, "This operation can only be performed on channels."); return false; } if (parent.changesHaveBeenMade()) { if (parent.alertOption(this, "This channel has been modified. You must save the channel changes before you can export. Would you like to save them now?")) { if (!parent.channelEditPanel.saveChanges()) { return false; } } else { return false; } parent.setSaveEnabled(false); } Channel channel; if (parent.currentContentPage == parent.channelEditPanel || parent.currentContentPage == parent.channelEditPanel.filterPane || parent.currentContentPage == parent.channelEditPanel.transformerPane) { channel = parent.channelEditPanel.currentChannel; } else { List<Channel> selectedChannels = getSelectedChannels(); if (selectedChannels.size() > 1) { exportChannels(selectedChannels); return true; } channel = selectedChannels.get(0); } // Add code template libraries if necessary if (channelHasLinkedCodeTemplates(channel)) { boolean addLibraries = true; String exportChannelCodeTemplateLibraries = Preferences.userNodeForPackage(Mirth.class) .get("exportChannelCodeTemplateLibraries", null); if (exportChannelCodeTemplateLibraries == null) { ExportChannelLibrariesDialog dialog = new ExportChannelLibrariesDialog(channel); if (dialog.getResult() == JOptionPane.NO_OPTION) { addLibraries = false; } else if (dialog.getResult() != JOptionPane.YES_OPTION) { return false; } } else { addLibraries = Boolean.parseBoolean(exportChannelCodeTemplateLibraries); } if (addLibraries) { addCodeTemplateLibrariesToChannel(channel); } } addDependenciesToChannel(channel); // Update resource names parent.updateResourceNames(channel); ObjectXMLSerializer serializer = ObjectXMLSerializer.getInstance(); String channelXML = serializer.serialize(channel); // Reset the libraries on the cached channel channel.getCodeTemplateLibraries().clear(); channel.clearDependencies(); return parent.exportFile(channelXML, channel.getName(), "XML", "Channel"); } private void exportChannels(List<Channel> channelList) { if (channelHasLinkedCodeTemplates(channelList)) { boolean addLibraries; String exportChannelCodeTemplateLibraries = Preferences.userNodeForPackage(Mirth.class) .get("exportChannelCodeTemplateLibraries", null); if (exportChannelCodeTemplateLibraries == null) { JCheckBox alwaysChooseCheckBox = new JCheckBox( "Always choose this option by default in the future (may be changed in the Administrator settings)"); Object[] params = new Object[] { "<html>One or more channels has code template libraries linked to them.<br/>Do you wish to include these libraries in each respective channel export?</html>", alwaysChooseCheckBox }; int result = JOptionPane.showConfirmDialog(this, params, "Select an Option", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.YES_OPTION || result == JOptionPane.NO_OPTION) { addLibraries = result == JOptionPane.YES_OPTION; if (alwaysChooseCheckBox.isSelected()) { Preferences.userNodeForPackage(Mirth.class).putBoolean("exportChannelCodeTemplateLibraries", addLibraries); } } else { return; } } else { addLibraries = Boolean.parseBoolean(exportChannelCodeTemplateLibraries); } if (addLibraries) { for (Channel channel : channelList) { addCodeTemplateLibrariesToChannel(channel); } } } for (Channel channel : channelList) { addDependenciesToChannel(channel); } JFileChooser exportFileChooser = new JFileChooser(); exportFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); File currentDir = new File(Frame.userPreferences.get("currentDirectory", "")); if (currentDir.exists()) { exportFileChooser.setCurrentDirectory(currentDir); } int returnVal = exportFileChooser.showSaveDialog(this); File exportFile = null; File exportDirectory = null; String exportPath = "/"; if (returnVal == JFileChooser.APPROVE_OPTION) { Frame.userPreferences.put("currentDirectory", exportFileChooser.getCurrentDirectory().getPath()); int exportCollisionCount = 0; exportDirectory = exportFileChooser.getSelectedFile(); exportPath = exportDirectory.getAbsolutePath(); for (Channel channel : channelList) { exportFile = new File(exportPath + "/" + channel.getName() + ".xml"); if (exportFile.exists()) { exportCollisionCount++; } // Update resource names parent.updateResourceNames(channel); } try { int exportCount = 0; boolean overwriteAll = false; boolean skipAll = false; for (int i = 0, size = channelList.size(); i < size; i++) { Channel channel = channelList.get(i); exportFile = new File(exportPath + "/" + channel.getName() + ".xml"); boolean fileExists = exportFile.exists(); if (fileExists) { if (!overwriteAll && !skipAll) { if (exportCollisionCount == 1) { if (!parent.alertOption(parent, "The file " + channel.getName() + ".xml already exists. Would you like to overwrite it?")) { continue; } } else { ConflictOption conflictStatus = parent.alertConflict(parent, "<html>The file " + channel.getName() + ".xml already exists.<br>Would you like to overwrite it?</html>", exportCollisionCount); if (conflictStatus == ConflictOption.YES_APPLY_ALL) { overwriteAll = true; } else if (conflictStatus == ConflictOption.NO) { exportCollisionCount--; continue; } else if (conflictStatus == ConflictOption.NO_APPLY_ALL) { skipAll = true; continue; } } } exportCollisionCount--; } if (!fileExists || !skipAll) { String channelXML = ObjectXMLSerializer.getInstance().serialize(channel); FileUtils.writeStringToFile(exportFile, channelXML, UIConstants.CHARSET); exportCount++; } } if (exportCount > 0) { parent.alertInformation(parent, exportCount + " files were written successfully to " + exportPath + "."); } } catch (IOException ex) { parent.alertError(parent, "File could not be written."); } } // Reset the libraries on the cached channels for (Channel channel : channelList) { channel.getCodeTemplateLibraries().clear(); channel.clearDependencies(); } } private boolean channelHasLinkedCodeTemplates(Channel channel) { return channelHasLinkedCodeTemplates(Collections.singletonList(channel)); } private boolean channelHasLinkedCodeTemplates(List<Channel> channels) { for (Channel channel : channels) { for (CodeTemplateLibrary library : parent.codeTemplatePanel.getCachedCodeTemplateLibraries().values()) { if (library.getEnabledChannelIds().contains(channel.getId()) || (library.isIncludeNewChannels() && !library.getDisabledChannelIds().contains(channel.getId()))) { return true; } } } return false; } private boolean groupHasLinkedCodeTemplates(List<ChannelGroup> groups) { for (ChannelGroup group : groups) { if (channelHasLinkedCodeTemplates(group.getChannels())) { return true; } } return false; } private void addCodeTemplateLibrariesToChannel(Channel channel) { List<CodeTemplateLibrary> channelLibraries = new ArrayList<CodeTemplateLibrary>(); for (CodeTemplateLibrary library : parent.codeTemplatePanel.getCachedCodeTemplateLibraries().values()) { if (library.getEnabledChannelIds().contains(channel.getId()) || (library.isIncludeNewChannels() && !library.getDisabledChannelIds().contains(channel.getId()))) { library = new CodeTemplateLibrary(library); List<CodeTemplate> codeTemplates = new ArrayList<CodeTemplate>(); for (CodeTemplate codeTemplate : library.getCodeTemplates()) { codeTemplate = parent.codeTemplatePanel.getCachedCodeTemplates().get(codeTemplate.getId()); if (codeTemplate != null) { codeTemplates.add(codeTemplate); } } library.setCodeTemplates(codeTemplates); channelLibraries.add(library); } } channel.setCodeTemplateLibraries(channelLibraries); } private void addDependenciesToChannel(Channel channel) { Set<String> dependentIds = new HashSet<String>(); Set<String> dependencyIds = new HashSet<String>(); for (ChannelDependency channelDependency : getCachedChannelDependencies()) { if (StringUtils.equals(channelDependency.getDependencyId(), channel.getId())) { dependentIds.add(channelDependency.getDependentId()); } else if (StringUtils.equals(channelDependency.getDependentId(), channel.getId())) { dependencyIds.add(channelDependency.getDependencyId()); } } if (CollectionUtils.isNotEmpty(dependentIds)) { channel.setDependentIds(dependentIds); } if (CollectionUtils.isNotEmpty(dependencyIds)) { channel.setDependencyIds(dependencyIds); } } public boolean doExportAllGroups() { if (isSaveEnabled() && !promptSave(true)) { return false; } ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); if (!model.isGroupModeEnabled()) { return false; } MutableTreeTableNode root = (MutableTreeTableNode) model.getRoot(); if (root == null) { return false; } List<ChannelGroup> groups = new ArrayList<ChannelGroup>(); for (Enumeration<? extends MutableTreeTableNode> groupNodes = root.children(); groupNodes .hasMoreElements();) { AbstractChannelTableNode groupNode = (AbstractChannelTableNode) groupNodes.nextElement(); if (groupNode.isGroupNode()) { groups.add(new ChannelGroup(groupNode.getGroupStatus().getGroup())); } } return handleExportGroups(groups); } public boolean doExportGroup() { if (isSaveEnabled() && !promptSave(true)) { return false; } if (isChannelSelected()) { JOptionPane.showMessageDialog(parent, "This operation can only be performed on groups."); return false; } int[] rows = channelTable.getSelectedModelRows(); if (rows.length > 0) { List<ChannelGroup> groups = new ArrayList<ChannelGroup>(); // Populate list of groups with full channels for (int row : rows) { AbstractChannelTableNode groupNode = (AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent(); if (groupNode.isGroupNode()) { groups.add(new ChannelGroup(groupNode.getGroupStatus().getGroup())); } } return handleExportGroups(groups); } return false; } private boolean handleExportGroups(List<ChannelGroup> groups) { // Populate list of groups with full channels for (ChannelGroup group : groups) { List<Channel> channels = new ArrayList<Channel>(); for (Channel channel : group.getChannels()) { ChannelStatus channelStatus = this.channelStatuses.get(channel.getId()); if (channelStatus != null) { channels.add(channelStatus.getChannel()); } } group.setChannels(channels); } try { // Add code template libraries to channels if necessary if (groupHasLinkedCodeTemplates(groups)) { boolean addLibraries; String exportChannelCodeTemplateLibraries = Preferences.userNodeForPackage(Mirth.class) .get("exportChannelCodeTemplateLibraries", null); if (exportChannelCodeTemplateLibraries == null) { JCheckBox alwaysChooseCheckBox = new JCheckBox( "Always choose this option by default in the future (may be changed in the Administrator settings)"); Object[] params = new Object[] { "<html>One or more channels has code template libraries linked to them.<br/>Do you wish to include these libraries in each respective channel export?</html>", alwaysChooseCheckBox }; int result = JOptionPane.showConfirmDialog(this, params, "Select an Option", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.YES_OPTION || result == JOptionPane.NO_OPTION) { addLibraries = result == JOptionPane.YES_OPTION; if (alwaysChooseCheckBox.isSelected()) { Preferences.userNodeForPackage(Mirth.class) .putBoolean("exportChannelCodeTemplateLibraries", addLibraries); } } else { return false; } } else { addLibraries = Boolean.parseBoolean(exportChannelCodeTemplateLibraries); } if (addLibraries) { for (ChannelGroup group : groups) { for (Channel channel : group.getChannels()) { addCodeTemplateLibrariesToChannel(channel); } } } } // Update resource names for (ChannelGroup group : groups) { for (Channel channel : group.getChannels()) { addDependenciesToChannel(channel); parent.updateResourceNames(channel); } } if (groups.size() == 1) { return exportGroup(groups.iterator().next()); } else { return exportGroups(groups); } } finally { // Reset the libraries on the cached channels for (ChannelGroup group : groups) { for (Channel channel : group.getChannels()) { channel.getCodeTemplateLibraries().clear(); channel.clearDependencies(); } } } } private boolean exportGroup(ChannelGroup group) { ObjectXMLSerializer serializer = ObjectXMLSerializer.getInstance(); String groupXML = serializer.serialize(group); return parent.exportFile(groupXML, group.getName().replaceAll("[^a-zA-Z_0-9\\-\\s]", ""), "XML", "Channel group"); } private boolean exportGroups(List<ChannelGroup> groups) { JFileChooser exportFileChooser = new JFileChooser(); exportFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); File currentDir = new File(Frame.userPreferences.get("currentDirectory", "")); if (currentDir.exists()) { exportFileChooser.setCurrentDirectory(currentDir); } int returnVal = exportFileChooser.showSaveDialog(this); File exportFile = null; File exportDirectory = null; String exportPath = "/"; if (returnVal == JFileChooser.APPROVE_OPTION) { Frame.userPreferences.put("currentDirectory", exportFileChooser.getCurrentDirectory().getPath()); int exportCollisionCount = 0; exportDirectory = exportFileChooser.getSelectedFile(); exportPath = exportDirectory.getAbsolutePath(); for (ChannelGroup group : groups) { exportFile = new File( exportPath + "/" + group.getName().replaceAll("[^a-zA-Z_0-9\\-\\s]", "") + ".xml"); if (exportFile.exists()) { exportCollisionCount++; } } try { int exportCount = 0; boolean overwriteAll = false; boolean skipAll = false; for (int i = 0, size = groups.size(); i < size; i++) { ChannelGroup group = groups.get(i); String groupName = group.getName().replaceAll("[^a-zA-Z_0-9\\-\\s]", ""); exportFile = new File(exportPath + "/" + groupName + ".xml"); boolean fileExists = exportFile.exists(); if (fileExists) { if (!overwriteAll && !skipAll) { if (exportCollisionCount == 1) { if (!parent.alertOption(parent, "The file " + groupName + ".xml already exists. Would you like to overwrite it?")) { continue; } } else { ConflictOption conflictStatus = parent.alertConflict(parent, "<html>The file " + groupName + ".xml already exists.<br>Would you like to overwrite it?</html>", exportCollisionCount); if (conflictStatus == ConflictOption.YES_APPLY_ALL) { overwriteAll = true; } else if (conflictStatus == ConflictOption.NO) { exportCollisionCount--; continue; } else if (conflictStatus == ConflictOption.NO_APPLY_ALL) { skipAll = true; continue; } } } exportCollisionCount--; } if (!fileExists || !skipAll) { String groupXML = ObjectXMLSerializer.getInstance().serialize(group); FileUtils.writeStringToFile(exportFile, groupXML, UIConstants.CHARSET); exportCount++; } } if (exportCount > 0) { parent.alertInformation(parent, exportCount + " files were written successfully to " + exportPath + "."); return true; } } catch (IOException ex) { parent.alertError(parent, "File could not be written."); } } return false; } public void doDeleteGroup() { if (isChannelSelected()) { JOptionPane.showMessageDialog(parent, "This operation can only be performed on groups."); return; } ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); int[] rows = channelTable.getSelectedModelRows(); if (rows.length >= 0) { List<AbstractChannelTableNode> groupNodes = new ArrayList<AbstractChannelTableNode>(); for (int row : rows) { AbstractChannelTableNode groupNode = (AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent(); if (groupNode.isGroupNode()) { if (!StringUtils.equals(groupNode.getGroupStatus().getGroup().getId(), ChannelGroup.DEFAULT_ID)) { groupNodes.add(groupNode); } } } for (AbstractChannelTableNode groupNode : groupNodes) { Set<String> channelIds = new HashSet<String>(); for (Enumeration<? extends MutableTreeTableNode> channelNodes = groupNode.children(); channelNodes .hasMoreElements();) { channelIds.add(((AbstractChannelTableNode) channelNodes.nextElement()).getChannelStatus() .getChannel().getId()); } for (String channelId : channelIds) { model.removeChannelFromGroup(groupNode, channelId); } model.removeGroup(groupNode); } parent.setSaveEnabled(true); } } public void doDeleteChannel() { if (isSaveEnabled() && !promptSave(true)) { return; } if (isGroupSelected()) { JOptionPane.showMessageDialog(parent, "This operation can only be performed on channels."); return; } final List<Channel> selectedChannels = getSelectedChannels(); if (selectedChannels.size() == 0) { return; } if (!parent.alertOption(parent, "Are you sure you want to delete the selected channel(s)?\nAny selected deployed channel(s) will first be undeployed.")) { return; } final String workingId = parent.startWorking("Deleting channel..."); SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { public Void doInBackground() { Set<String> channelIds = new HashSet<String>(selectedChannels.size()); for (Channel channel : selectedChannels) { channelIds.add(channel.getId()); } try { parent.mirthClient.removeChannels(channelIds); } catch (ClientException e) { parent.alertThrowable(parent, e); } return null; } public void done() { doRefreshChannels(); parent.stopWorking(workingId); } }; worker.execute(); } public void doCloneChannel() { if (isSaveEnabled() && !promptSave(true)) { return; } if (isGroupSelected()) { JOptionPane.showMessageDialog(parent, "This operation can only be performed on channels."); return; } List<Channel> selectedChannels = getSelectedChannels(); if (selectedChannels.size() > 1) { JOptionPane.showMessageDialog(parent, "This operation can only be performed on a single channel."); return; } Channel channel = selectedChannels.get(0); if (channel instanceof InvalidChannel) { InvalidChannel invalidChannel = (InvalidChannel) channel; Throwable cause = invalidChannel.getCause(); parent.alertThrowable(parent, cause, "Channel \"" + channel.getName() + "\" is invalid and cannot be cloned. " + getMissingExtensions(invalidChannel) + "Original cause:\n" + cause.getMessage()); return; } try { channel = (Channel) SerializationUtils.clone(channel); } catch (SerializationException e) { parent.alertThrowable(parent, e); return; } try { channel.setRevision(0); channel.setId(parent.mirthClient.getGuid()); } catch (ClientException e) { parent.alertThrowable(parent, e); } String channelName = channel.getName(); do { channelName = JOptionPane.showInputDialog(this, "Please enter a new name for the channel.", channelName); if (channelName == null) { return; } } while (!parent.checkChannelName(channelName, channel.getId())); channel.setName(channelName); channelStatuses.put(channel.getId(), new ChannelStatus(channel)); parent.updateChannelTags(false); parent.editChannel(channel); parent.setSaveEnabled(true); } public void doEditChannel() { if (isSaveEnabled() && !confirmLeave()) { return; } if (parent.isEditingChannel) { return; } else { parent.isEditingChannel = true; } if (isGroupSelected()) { JOptionPane.showMessageDialog(parent, "This operation can only be performed on channels."); return; } List<Channel> selectedChannels = getSelectedChannels(); if (selectedChannels.size() > 1) { JOptionPane.showMessageDialog(parent, "This operation can only be performed on a single channel."); } else if (selectedChannels.size() == 0) { JOptionPane.showMessageDialog(parent, "Channel no longer exists."); } else { try { Channel channel = selectedChannels.get(0); if (channel instanceof InvalidChannel) { InvalidChannel invalidChannel = (InvalidChannel) channel; Throwable cause = invalidChannel.getCause(); parent.alertThrowable(parent, cause, "Channel \"" + channel.getName() + "\" is invalid and cannot be edited. " + getMissingExtensions(invalidChannel) + "Original cause:\n" + cause.getMessage()); } else { parent.editChannel((Channel) SerializationUtils.clone(channel)); } } catch (SerializationException e) { parent.alertThrowable(parent, e); } } parent.isEditingChannel = false; } public void doEnableChannel() { if (isSaveEnabled() && !promptSave(true)) { return; } final List<Channel> selectedChannels = getSelectedChannels(); if (selectedChannels.size() == 0) { parent.alertWarning(parent, "Channel no longer exists."); return; } final Set<String> channelIds = new HashSet<String>(); Set<Channel> failedChannels = new HashSet<Channel>(); String firstValidationMessage = null; for (Iterator<Channel> it = selectedChannels.iterator(); it.hasNext();) { Channel channel = it.next(); String validationMessage = null; if (channel instanceof InvalidChannel) { failedChannels.add(channel); it.remove(); } else if ((validationMessage = parent.channelEditPanel.checkAllForms(channel)) != null) { if (firstValidationMessage == null) { firstValidationMessage = validationMessage; } failedChannels.add(channel); it.remove(); } else { channelIds.add(channel.getId()); } } if (!channelIds.isEmpty()) { final String workingId = parent.startWorking("Enabling channel..."); SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { public Void doInBackground() { for (Channel channel : selectedChannels) { channel.setEnabled(true); } try { parent.mirthClient.setChannelEnabled(channelIds, true); } catch (ClientException e) { parent.alertThrowable(parent, e); } return null; } public void done() { doRefreshChannels(); parent.stopWorking(workingId); } }; worker.execute(); } if (!failedChannels.isEmpty()) { if (failedChannels.size() == 1) { Channel channel = failedChannels.iterator().next(); if (channel instanceof InvalidChannel) { InvalidChannel invalidChannel = (InvalidChannel) channel; Throwable cause = invalidChannel.getCause(); parent.alertThrowable(parent, cause, "Channel \"" + invalidChannel.getName() + "\" is invalid and cannot be enabled. " + getMissingExtensions(invalidChannel) + "Original cause:\n" + cause.getMessage()); } else { parent.alertCustomError(parent, firstValidationMessage, CustomErrorDialog.ERROR_ENABLING_CHANNEL); } } else { String message = "The following channels are invalid or not configured properly:\n\n"; for (Channel channel : failedChannels) { message += " " + channel.getName() + " (" + channel.getId() + ")\n"; } parent.alertError(parent, message); } } } public void doDisableChannel() { if (isSaveEnabled() && !promptSave(true)) { return; } final List<Channel> selectedChannels = getSelectedChannels(); if (selectedChannels.size() == 0) { parent.alertWarning(parent, "Channel no longer exists."); return; } final String workingId = parent.startWorking("Disabling channels..."); SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { public Void doInBackground() { Set<String> channelIds = new HashSet<String>(); for (Channel channel : selectedChannels) { if (!(channel instanceof InvalidChannel)) { channel.setEnabled(false); channelIds.add(channel.getId()); } } try { parent.mirthClient.setChannelEnabled(channelIds, false); } catch (ClientException e) { parent.alertThrowable(parent, e); } return null; } public void done() { doRefreshChannels(); parent.stopWorking(workingId); } }; worker.execute(); } public void doViewMessages() { if (isSaveEnabled() && !confirmLeave()) { return; } parent.doShowMessages(); } public static int getNumberOfDefaultColumns() { return DEFAULT_COLUMNS.length; } private void setChannelTaskVisible(int task) { setChannelTaskVisibility(task, true); } private void setChannelTaskVisibility(int task, boolean visible) { parent.setVisibleTasks(channelTasks, channelPopupMenu, task, task, visible); } private void setGroupTaskVisible(int task) { setGroupTaskVisibility(task, true); } private void setGroupTaskVisibility(int task, boolean visible) { parent.setVisibleTasks(groupTasks, groupPopupMenu, task, task, visible); } private void setAllTaskVisibility(boolean visible) { parent.setVisibleTasks(channelTasks, channelPopupMenu, 1, TASK_CHANNEL_VIEW_MESSAGES, visible); parent.setVisibleTasks(groupTasks, groupPopupMenu, 1, TASK_GROUP_DELETE_GROUP, visible); } public void updateChannelStatuses(List<ChannelSummary> changedChannels) { for (ChannelSummary channelSummary : changedChannels) { String channelId = channelSummary.getChannelId(); if (channelSummary.isDeleted()) { channelStatuses.remove(channelId); } else { ChannelStatus channelStatus = channelStatuses.get(channelId); if (channelStatus == null) { channelStatus = new ChannelStatus(); channelStatuses.put(channelId, channelStatus); } /* * If the status coming back from the server is for an entirely new channel, the * Channel object should never be null. */ if (channelSummary.getChannelStatus().getChannel() != null) { channelStatus.setChannel(channelSummary.getChannelStatus().getChannel()); } if (channelSummary.isUndeployed()) { channelStatus.setDeployedDate(null); channelStatus.setDeployedRevisionDelta(null); channelStatus.setCodeTemplatesChanged(false); } else { if (channelSummary.getChannelStatus().getDeployedDate() != null) { channelStatus.setDeployedDate(channelSummary.getChannelStatus().getDeployedDate()); channelStatus.setDeployedRevisionDelta( channelSummary.getChannelStatus().getDeployedRevisionDelta()); } channelStatus .setCodeTemplatesChanged(channelSummary.getChannelStatus().isCodeTemplatesChanged()); } channelStatus.setLocalChannelId(channelSummary.getChannelStatus().getLocalChannelId()); } } parent.updateChannelTags(false); } private void updateChannelGroups(List<ChannelGroup> channelGroups) { if (channelGroups != null) { this.groupStatuses.clear(); ChannelGroup defaultGroup = ChannelGroup.getDefaultGroup(); List<ChannelStatus> defaultGroupChannelStatuses = new ArrayList<ChannelStatus>(); ChannelGroupStatus defaultGroupStatus = new ChannelGroupStatus(defaultGroup, defaultGroupChannelStatuses); this.groupStatuses.put(defaultGroup.getId(), defaultGroupStatus); Set<String> visitedChannelIds = new HashSet<String>(); Set<String> remainingChannelIds = new HashSet<String>(this.channelStatuses.keySet()); for (ChannelGroup group : channelGroups) { List<ChannelStatus> channelStatuses = new ArrayList<ChannelStatus>(); for (Channel channel : group.getChannels()) { if (!visitedChannelIds.contains(channel.getId())) { ChannelStatus channelStatus = this.channelStatuses.get(channel.getId()); if (channelStatus != null) { channelStatuses.add(channelStatus); } visitedChannelIds.add(channel.getId()); remainingChannelIds.remove(channel.getId()); } } this.groupStatuses.put(group.getId(), new ChannelGroupStatus(group, channelStatuses)); } for (String channelId : remainingChannelIds) { defaultGroup.getChannels().add(new Channel(channelId)); defaultGroupChannelStatuses.add(this.channelStatuses.get(channelId)); } } } public void clearChannelCache() { channelStatuses = new HashMap<String, ChannelStatus>(); groupStatuses = new HashMap<String, ChannelGroupStatus>(); parent.updateChannelTags(false); } private String getMissingExtensions(InvalidChannel channel) { Set<String> missingConnectors = new HashSet<String>(); Set<String> missingDataTypes = new HashSet<String>(); try { DonkeyElement channelElement = new DonkeyElement(((InvalidChannel) channel).getChannelXml()); checkConnectorForMissingExtensions(channelElement.getChildElement("sourceConnector"), true, missingConnectors, missingDataTypes); DonkeyElement destinationConnectors = channelElement.getChildElement("destinationConnectors"); if (destinationConnectors != null) { for (DonkeyElement destinationConnector : destinationConnectors.getChildElements()) { checkConnectorForMissingExtensions(destinationConnector, false, missingConnectors, missingDataTypes); } } } catch (DonkeyElementException e) { } StringBuilder builder = new StringBuilder(); if (!missingConnectors.isEmpty()) { builder.append( "\n\nYour Mirth Connect installation is missing required connectors for this channel:\n "); builder.append(StringUtils.join(missingConnectors.toArray(), "\n ")); builder.append("\n\n"); } if (!missingDataTypes.isEmpty()) { if (missingConnectors.isEmpty()) { builder.append("\n\n"); } builder.append( "Your Mirth Connect installation is missing required data types for this channel:\n "); builder.append(StringUtils.join(missingDataTypes.toArray(), "\n ")); builder.append("\n\n"); } return builder.toString(); } private void checkConnectorForMissingExtensions(DonkeyElement connector, boolean source, Set<String> missingConnectors, Set<String> missingDataTypes) { if (connector != null) { DonkeyElement transportName = connector.getChildElement("transportName"); // Check for 2.x-specific connectors transportName.setTextContent(ImportConverter3_0_0.convertTransportName(transportName.getTextContent())); if (transportName != null) { if (source && !LoadedExtensions.getInstance().getSourceConnectors() .containsKey(transportName.getTextContent())) { missingConnectors.add(transportName.getTextContent()); } else if (!source && !LoadedExtensions.getInstance().getDestinationConnectors() .containsKey(transportName.getTextContent())) { missingConnectors.add(transportName.getTextContent()); } } checkTransformerForMissingExtensions(connector.getChildElement("transformer"), missingDataTypes); if (!source) { checkTransformerForMissingExtensions(connector.getChildElement("responseTransformer"), missingDataTypes); } } } private void checkTransformerForMissingExtensions(DonkeyElement transformer, Set<String> missingDataTypes) { if (transformer != null) { // Check for 2.x-specific data types missingDataTypes.addAll(ImportConverter3_0_0.getMissingDataTypes(transformer, LoadedExtensions.getInstance().getDataTypePlugins().keySet())); DonkeyElement inboundDataType = transformer.getChildElement("inboundDataType"); if (inboundDataType != null && !LoadedExtensions.getInstance().getDataTypePlugins() .containsKey(inboundDataType.getTextContent())) { missingDataTypes.add(inboundDataType.getTextContent()); } DonkeyElement outboundDataType = transformer.getChildElement("outboundDataType"); if (outboundDataType != null && !LoadedExtensions.getInstance().getDataTypePlugins() .containsKey(outboundDataType.getTextContent())) { missingDataTypes.add(outboundDataType.getTextContent()); } } } public void initPanelPlugins() { loadPanelPlugins(); switchBottomPane(); ChangeListener changeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent changeEvent) { JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent.getSource(); int index = sourceTabbedPane.getSelectedIndex(); loadPanelPlugin(sourceTabbedPane.getTitleAt(index)); } }; tabPane.addChangeListener(changeListener); } private void switchBottomPane() { if (LoadedExtensions.getInstance().getChannelPanelPlugins().size() > 0) { splitPane.setBottomComponent(tabPane); splitPane.setDividerSize(6); splitPane.setDividerLocation( 3 * Preferences.userNodeForPackage(Mirth.class).getInt("height", UIConstants.MIRTH_HEIGHT) / 5); splitPane.setResizeWeight(0.5); } else { splitPane.setBottomComponent(null); splitPane.setDividerSize(0); } } private void loadPanelPlugins() { if (LoadedExtensions.getInstance().getChannelPanelPlugins().size() > 0) { for (ChannelPanelPlugin plugin : LoadedExtensions.getInstance().getChannelPanelPlugins().values()) { if (plugin.getComponent() != null) { tabPane.addTab(plugin.getPluginPointName(), plugin.getComponent()); } } } } private void loadPanelPlugin(String pluginName) { final ChannelPanelPlugin plugin = LoadedExtensions.getInstance().getChannelPanelPlugins().get(pluginName); if (plugin != null) { final List<Channel> selectedChannels = getSelectedChannels(); QueuingSwingWorkerTask<Void, Void> task = new QueuingSwingWorkerTask<Void, Void>(pluginName, "Updating " + pluginName + " channel panel plugin...") { @Override public Void doInBackground() { try { if (selectedChannels.size() > 0) { plugin.prepareData(selectedChannels); } else { plugin.prepareData(); } } catch (ClientException e) { parent.alertThrowable(parent, e); } return null; } @Override public void done() { if (selectedChannels.size() > 0) { plugin.update(selectedChannels); } else { plugin.update(); } } }; new QueuingSwingWorker<Void, Void>(task, true).executeDelegate(); } } private synchronized void updateCurrentPluginPanel() { if (LoadedExtensions.getInstance().getChannelPanelPlugins().size() > 0) { loadPanelPlugin(tabPane.getTitleAt(tabPane.getSelectedIndex())); } } private synchronized void updateModel(TableState tableState) { ChannelTagInfo channelTagInfo = parent.getChannelTagInfo(false); List<ChannelStatus> filteredChannelStatuses = new ArrayList<ChannelStatus>(); int enabled = 0; for (ChannelStatus channelStatus : channelStatuses.values()) { Channel channel = channelStatus.getChannel(); if (!channelTagInfo.isEnabled() || CollectionUtils.containsAny(channelTagInfo.getVisibleTags(), channel.getProperties().getTags())) { filteredChannelStatuses.add(channelStatus); if (channel.isEnabled()) { enabled++; } } } int totalChannelCount = channelStatuses.size(); int visibleChannelCount = filteredChannelStatuses.size(); List<Channel> filteredChannels = new ArrayList<Channel>(); for (ChannelStatus filteredChannelStatus : filteredChannelStatuses) { filteredChannels.add(filteredChannelStatus.getChannel()); } List<ChannelGroupStatus> filteredGroupStatuses = new ArrayList<ChannelGroupStatus>(); for (ChannelGroupStatus groupStatus : groupStatuses.values()) { filteredGroupStatuses.add(new ChannelGroupStatus(groupStatus)); } int totalGroupCount = filteredGroupStatuses.size(); int visibleGroupCount = totalGroupCount; for (Iterator<ChannelGroupStatus> groupStatusIterator = filteredGroupStatuses .iterator(); groupStatusIterator.hasNext();) { ChannelGroupStatus groupStatus = groupStatusIterator.next(); for (Iterator<ChannelStatus> channelStatusIterator = groupStatus.getChannelStatuses() .iterator(); channelStatusIterator.hasNext();) { ChannelStatus channelStatus = channelStatusIterator.next(); boolean found = false; for (ChannelStatus filteredChannelStatus : filteredChannelStatuses) { if (filteredChannelStatus.getChannel().getId().equals(channelStatus.getChannel().getId())) { found = true; break; } } if (!found) { channelStatusIterator.remove(); } } if (totalChannelCount != visibleChannelCount && groupStatus.getChannelStatuses().isEmpty()) { groupStatusIterator.remove(); visibleGroupCount--; } } ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); StringBuilder builder = new StringBuilder(); if (model.isGroupModeEnabled()) { if (totalGroupCount == visibleGroupCount) { builder.append(totalGroupCount); } else { builder.append(visibleGroupCount).append(" of ").append(totalGroupCount); } builder.append(" Group"); if (totalGroupCount != 1) { builder.append('s'); } if (totalGroupCount != visibleGroupCount) { builder.append(" (").append(totalGroupCount - visibleGroupCount).append(" filtered)"); } builder.append(", "); } if (totalChannelCount == visibleChannelCount) { builder.append(totalChannelCount); } else { builder.append(visibleChannelCount).append(" of ").append(totalChannelCount); } builder.append(" Channel"); if (totalChannelCount != 1) { builder.append('s'); } if (totalChannelCount != visibleChannelCount) { builder.append(" (").append(totalChannelCount - visibleChannelCount).append(" filtered)"); } builder.append(", ").append(enabled).append(" Enabled"); if (channelTagInfo.isEnabled()) { builder.append(" ("); for (Iterator<String> it = channelTagInfo.getVisibleTags().iterator(); it.hasNext();) { builder.append(it.next()); if (it.hasNext()) { builder.append(", "); } } builder.append(')'); } tagsLabel.setText(builder.toString()); for (ChannelColumnPlugin plugin : LoadedExtensions.getInstance().getChannelColumnPlugins().values()) { plugin.tableUpdate(filteredChannels); } model.update(filteredGroupStatuses); restoreTableState(tableState); } /** * Shows the popup menu when the trigger button (right-click) has been pushed. Deselects the * rows if no row was selected. */ private void checkSelectionAndPopupMenu(MouseEvent evt) { int row = channelTable.rowAtPoint(new Point(evt.getX(), evt.getY())); if (row == -1) { deselectRows(); } if (evt.isPopupTrigger()) { if (row != -1) { if (!channelTable.isRowSelected(row)) { channelTable.setRowSelectionInterval(row, row); } if (((AbstractChannelTableNode) channelTable.getPathForRow(row).getLastPathComponent()) .isGroupNode()) { groupPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); } else { channelPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); } } else { channelPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); } } } /** The action called when a Channel is selected. Sets tasks as well. */ private void channelListSelected(ListSelectionEvent evt) { updateTasks(); int[] rows = channelTable.getSelectedModelRows(); if (rows.length > 0) { for (TaskPlugin plugin : LoadedExtensions.getInstance().getTaskPlugins().values()) { plugin.onRowSelected(channelTable); } updateCurrentPluginPanel(); } } public boolean isGroupSelected() { for (int row : channelTable.getSelectedModelRows()) { AbstractChannelTableNode node = (AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent(); if (node.isGroupNode()) { return true; } } return false; } public boolean isChannelSelected() { for (int row : channelTable.getSelectedModelRows()) { AbstractChannelTableNode node = (AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent(); if (!node.isGroupNode()) { return true; } } return false; } public List<Channel> getSelectedChannels() { List<Channel> selectedChannels = new ArrayList<Channel>(); for (int row : channelTable.getSelectedModelRows()) { AbstractChannelTableNode node = (AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent(); if (node.isGroupNode()) { for (Enumeration<? extends MutableTreeTableNode> channelNodes = node.children(); channelNodes .hasMoreElements();) { selectedChannels.add(((AbstractChannelTableNode) channelNodes.nextElement()).getChannelStatus() .getChannel()); } } else { selectedChannels.add(node.getChannelStatus().getChannel()); } } return selectedChannels; } private void deselectRows() { channelTable.clearSelection(); updateTasks(); for (TaskPlugin plugin : LoadedExtensions.getInstance().getTaskPlugins().values()) { plugin.onRowDeselected(); } updateCurrentPluginPanel(); } /** * Checks to see if the passed in channel id already exists */ private boolean checkChannelId(String id) { for (ChannelStatus channelStatus : channelStatuses.values()) { if (channelStatus.getChannel().getId().equalsIgnoreCase(id)) { return false; } } return true; } private static class DefaultChannelTableNodeFactory implements ChannelTableNodeFactory { @Override public AbstractChannelTableNode createNode(ChannelGroupStatus groupStatus) { return new ChannelTableNode(groupStatus); } @Override public AbstractChannelTableNode createNode(ChannelStatus channelStatus) { return new ChannelTableNode(channelStatus); } } private void initComponents() { splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); splitPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); splitPane.setOneTouchExpandable(true); topPanel = new JPanel(); List<String> columns = new ArrayList<String>(); for (ChannelColumnPlugin plugin : LoadedExtensions.getInstance().getChannelColumnPlugins().values()) { if (plugin.isDisplayFirst()) { columns.add(plugin.getColumnHeader()); } } columns.addAll(Arrays.asList(DEFAULT_COLUMNS)); for (ChannelColumnPlugin plugin : LoadedExtensions.getInstance().getChannelColumnPlugins().values()) { if (!plugin.isDisplayFirst()) { columns.add(plugin.getColumnHeader()); } } channelTable = new MirthTreeTable("channelPanel", new LinkedHashSet<String>(columns)); channelTable.setColumnFactory(new ChannelTableColumnFactory()); ChannelTreeTableModel model = new ChannelTreeTableModel(); model.setColumnIdentifiers(columns); model.setNodeFactory(new DefaultChannelTableNodeFactory()); channelTable.setTreeTableModel(model); channelTable.setDoubleBuffered(true); channelTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); channelTable.getTreeSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); channelTable.setHorizontalScrollEnabled(true); channelTable.packTable(UIConstants.COL_MARGIN); channelTable.setRowHeight(UIConstants.ROW_HEIGHT); channelTable.setOpaque(true); channelTable.setRowSelectionAllowed(true); channelTable.setSortable(true); channelTable.putClientProperty("JTree.lineStyle", "Horizontal"); channelTable.setAutoCreateColumnsFromModel(false); channelTable.setShowGrid(true, true); channelTable.restoreColumnPreferences(); channelTable.setMirthColumnControlEnabled(true); channelTable.setDragEnabled(true); channelTable.setDropMode(DropMode.ON); channelTable.setTransferHandler(new ChannelTableTransferHandler() { @Override public boolean canImport(TransferSupport support) { // Don't allow files to be imported when the save task is enabled if (support.isDataFlavorSupported(DataFlavor.javaFileListFlavor) && isSaveEnabled()) { return false; } return super.canImport(support); } @Override public void importFile(final File file, final boolean showAlerts) { try { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { String fileString = StringUtils.trim(parent.readFileToString(file)); try { // If the table is in channel view, don't allow groups to be imported ChannelGroup group = ObjectXMLSerializer.getInstance().deserialize(fileString, ChannelGroup.class); if (group != null && !((ChannelTreeTableModel) channelTable.getTreeTableModel()) .isGroupModeEnabled()) { return; } } catch (Exception e) { } if (showAlerts && !parent.promptObjectMigration(fileString, "channel or group")) { return; } try { importChannel( ObjectXMLSerializer.getInstance().deserialize(fileString, Channel.class), showAlerts); } catch (Exception e) { try { importGroup(ObjectXMLSerializer.getInstance().deserialize(fileString, ChannelGroup.class), showAlerts, !showAlerts); } catch (Exception e2) { if (showAlerts) { parent.alertThrowable(parent, e, "Invalid channel or group file:\n" + e.getMessage()); } } } } }); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean canMoveChannels(List<Channel> channels, int row) { if (row >= 0) { TreePath path = channelTable.getPathForRow(row); if (path != null) { AbstractChannelTableNode node = (AbstractChannelTableNode) path.getLastPathComponent(); if (node.isGroupNode()) { Set<String> currentChannelIds = new HashSet<String>(); for (Enumeration<? extends MutableTreeTableNode> channelNodes = node .children(); channelNodes.hasMoreElements();) { currentChannelIds.add(((AbstractChannelTableNode) channelNodes.nextElement()) .getChannelStatus().getChannel().getId()); } for (Iterator<Channel> it = channels.iterator(); it.hasNext();) { if (currentChannelIds.contains(it.next().getId())) { it.remove(); } } return !channels.isEmpty(); } } } return false; } @Override public boolean moveChannels(List<Channel> channels, int row) { if (row >= 0) { TreePath path = channelTable.getPathForRow(row); if (path != null) { AbstractChannelTableNode node = (AbstractChannelTableNode) path.getLastPathComponent(); if (node.isGroupNode()) { Set<String> currentChannelIds = new HashSet<String>(); for (Enumeration<? extends MutableTreeTableNode> channelNodes = node .children(); channelNodes.hasMoreElements();) { currentChannelIds.add(((AbstractChannelTableNode) channelNodes.nextElement()) .getChannelStatus().getChannel().getId()); } for (Iterator<Channel> it = channels.iterator(); it.hasNext();) { if (currentChannelIds.contains(it.next().getId())) { it.remove(); } } if (!channels.isEmpty()) { ListSelectionListener[] listeners = ((DefaultListSelectionModel) channelTable .getSelectionModel()).getListSelectionListeners(); for (ListSelectionListener listener : listeners) { channelTable.getSelectionModel().removeListSelectionListener(listener); } try { ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable .getTreeTableModel(); Set<String> channelIds = new HashSet<String>(); for (Channel channel : channels) { model.addChannelToGroup(node, channel.getId()); channelIds.add(channel.getId()); } List<TreePath> selectionPaths = new ArrayList<TreePath>(); for (Enumeration<? extends MutableTreeTableNode> channelNodes = node .children(); channelNodes.hasMoreElements();) { AbstractChannelTableNode channelNode = (AbstractChannelTableNode) channelNodes .nextElement(); if (channelIds .contains(channelNode.getChannelStatus().getChannel().getId())) { selectionPaths.add(new TreePath( new Object[] { model.getRoot(), node, channelNode })); } } parent.setSaveEnabled(true); channelTable.expandPath(new TreePath( new Object[] { channelTable.getTreeTableModel().getRoot(), node })); channelTable.getTreeSelectionModel().setSelectionPaths( selectionPaths.toArray(new TreePath[selectionPaths.size()])); return true; } finally { for (ListSelectionListener listener : listeners) { channelTable.getSelectionModel().addListSelectionListener(listener); } } } } } } return false; } }); channelTable.setTreeCellRenderer(new DefaultTreeCellRenderer() { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { JLabel label = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); TreePath path = channelTable.getPathForRow(row); if (path != null && ((AbstractChannelTableNode) path.getLastPathComponent()).isGroupNode()) { setIcon(UIConstants.ICON_GROUP); } return label; } }); channelTable.setLeafIcon(UIConstants.ICON_CHANNEL); channelTable.setOpenIcon(UIConstants.ICON_GROUP); channelTable.setClosedIcon(UIConstants.ICON_GROUP); channelTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent evt) { channelListSelected(evt); } }); // listen for trigger button and double click to edit channel. channelTable.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent evt) { checkSelectionAndPopupMenu(evt); } @Override public void mouseReleased(MouseEvent evt) { checkSelectionAndPopupMenu(evt); } @Override public void mouseClicked(MouseEvent evt) { int row = channelTable.rowAtPoint(new Point(evt.getX(), evt.getY())); if (row == -1) { return; } if (evt.getClickCount() >= 2 && channelTable.getSelectedRowCount() == 1 && channelTable.getSelectedRow() == row) { AbstractChannelTableNode node = (AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent(); if (node.isGroupNode()) { doEditGroupDetails(); } else { doEditChannel(); } } } }); // Key Listener trigger for DEL channelTable.addKeyListener(new KeyListener() { @Override public void keyPressed(KeyEvent evt) { if (evt.getKeyCode() == KeyEvent.VK_DELETE) { if (channelTable.getSelectedModelRows().length == 0) { return; } boolean allGroups = true; boolean allChannels = true; for (int row : channelTable.getSelectedModelRows()) { AbstractChannelTableNode node = (AbstractChannelTableNode) channelTable.getPathForRow(row) .getLastPathComponent(); if (node.isGroupNode()) { allChannels = false; } else { allGroups = false; } } if (allChannels) { doDeleteChannel(); } else if (allGroups) { doDeleteGroup(); } } } @Override public void keyReleased(KeyEvent evt) { } @Override public void keyTyped(KeyEvent evt) { } }); // MIRTH-2301 // Since we are using addHighlighter here instead of using setHighlighters, we need to remove the old ones first. channelTable.setHighlighters(); // Set highlighter. if (Preferences.userNodeForPackage(Mirth.class).getBoolean("highlightRows", true)) { Highlighter highlighter = HighlighterFactory.createAlternateStriping(UIConstants.HIGHLIGHTER_COLOR, UIConstants.BACKGROUND_COLOR); channelTable.addHighlighter(highlighter); } HighlightPredicate revisionDeltaHighlighterPredicate = new HighlightPredicate() { @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { if (adapter.column == channelTable.convertColumnIndexToView( channelTable.getColumnExt(DEPLOYED_REVISION_DELTA_COLUMN_NAME).getModelIndex())) { if (channelTable.getValueAt(adapter.row, adapter.column) != null && ((Integer) channelTable.getValueAt(adapter.row, adapter.column)).intValue() > 0) { return true; } if (channelStatuses != null) { String channelId = (String) channelTable.getModel() .getValueAt(channelTable.convertRowIndexToModel(adapter.row), ID_COLUMN_NUMBER); ChannelStatus status = channelStatuses.get(channelId); if (status != null && status.isCodeTemplatesChanged()) { return true; } } } return false; } }; channelTable.addHighlighter(new ColorHighlighter(revisionDeltaHighlighterPredicate, new Color(255, 204, 0), Color.BLACK, new Color(255, 204, 0), Color.BLACK)); HighlightPredicate lastDeployedHighlighterPredicate = new HighlightPredicate() { @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { if (adapter.column == channelTable.convertColumnIndexToView( channelTable.getColumnExt(LAST_DEPLOYED_COLUMN_NAME).getModelIndex())) { Calendar checkAfter = Calendar.getInstance(); checkAfter.add(Calendar.MINUTE, -2); if (channelTable.getValueAt(adapter.row, adapter.column) != null && ((Calendar) channelTable.getValueAt(adapter.row, adapter.column)) .after(checkAfter)) { return true; } } return false; } }; channelTable.addHighlighter(new ColorHighlighter(lastDeployedHighlighterPredicate, new Color(240, 230, 140), Color.BLACK, new Color(240, 230, 140), Color.BLACK)); channelScrollPane = new JScrollPane(channelTable); channelScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); filterPanel = new JPanel(); filterPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, new Color(164, 164, 164))); tagsFilterButton = new IconButton(); tagsFilterButton .setIcon(new ImageIcon(getClass().getResource("/com/mirth/connect/client/ui/images/wrench.png"))); tagsFilterButton.setToolTipText("Show Channel Filter"); tagsFilterButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { tagsFilterButtonActionPerformed(); } }); tagsLabel = new JLabel(); ButtonGroup tableModeButtonGroup = new ButtonGroup(); tableModeGroupsButton = new IconToggleButton(UIConstants.ICON_GROUP); tableModeGroupsButton.setToolTipText("Groups"); tableModeGroupsButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { if (!switchTableMode(true)) { tableModeChannelsButton.setSelected(true); } } }); tableModeButtonGroup.add(tableModeGroupsButton); tableModeChannelsButton = new IconToggleButton(UIConstants.ICON_CHANNEL); tableModeChannelsButton.setToolTipText("Channels"); tableModeChannelsButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { if (!switchTableMode(false)) { tableModeGroupsButton.setSelected(true); } } }); tableModeButtonGroup.add(tableModeChannelsButton); tabPane = new JTabbedPane(); splitPane.setTopComponent(topPanel); splitPane.setBottomComponent(tabPane); } private void initLayout() { setLayout(new MigLayout("insets 0, novisualpadding, hidemode 3, fill, gap 0")); topPanel.setLayout(new MigLayout("insets 0, novisualpadding, hidemode 3, fill, gap 0")); topPanel.add(channelScrollPane, "grow, push"); filterPanel.setLayout(new MigLayout("insets 0 12 0 12, novisualpadding, hidemode 3, fill, gap 12")); filterPanel.add(tagsFilterButton); filterPanel.add(tagsLabel, "left, growx, push"); filterPanel.add(tableModeGroupsButton, "right, split 2, gapafter 0"); filterPanel.add(tableModeChannelsButton); topPanel.add(filterPanel, "newline, growx"); add(splitPane, "grow, push"); } private void tagsFilterButtonActionPerformed() { if (isSaveEnabled() && !promptSave(true)) { return; } new ChannelFilter(parent.getChannelTagInfo(false), new ChannelFilterSaveTask() { @Override public void save(ChannelTagInfo channelTagInfo) { parent.setFilteredChannelTags(false, channelTagInfo.getVisibleTags(), channelTagInfo.isEnabled()); doRefreshChannels(true); } }); } private boolean switchTableMode(boolean groupModeEnabled) { return switchTableMode(groupModeEnabled, true); } private boolean switchTableMode(boolean groupModeEnabled, boolean promptSave) { ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); if (model.isGroupModeEnabled() != groupModeEnabled) { if (promptSave && isSaveEnabled() && !promptSave(true)) { return false; } Preferences.userNodeForPackage(Mirth.class).putBoolean("channelGroupViewEnabled", groupModeEnabled); List<JXTaskPane> taskPanes = new ArrayList<JXTaskPane>(); taskPanes.add(channelTasks); if (groupModeEnabled) { tableModeChannelsButton.setContentFilled(false); taskPanes.add(groupTasks); } else { tableModeGroupsButton.setContentFilled(false); } for (TaskPlugin plugin : LoadedExtensions.getInstance().getTaskPlugins().values()) { JXTaskPane taskPane = plugin.getTaskPane(); if (taskPane != null) { taskPanes.add(taskPane); } } parent.setFocus(taskPanes.toArray(new JXTaskPane[taskPanes.size()]), true, true); TableState tableState = getCurrentTableState(); model.setGroupModeEnabled(groupModeEnabled); updateModel(tableState); updateTasks(); } return true; } private TableState getCurrentTableState() { List<String> selectedIds = new ArrayList<String>(); List<String> expandedGroupIds = null; int[] selectedRows = channelTable.getSelectedModelRows(); for (int i = 0; i < selectedRows.length; i++) { AbstractChannelTableNode node = (AbstractChannelTableNode) channelTable.getPathForRow(selectedRows[i]) .getLastPathComponent(); if (node.isGroupNode()) { selectedIds.add(node.getGroupStatus().getGroup().getId()); } else { selectedIds.add(node.getChannelStatus().getChannel().getId()); } } ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); if (model.isGroupModeEnabled()) { MutableTreeTableNode root = (MutableTreeTableNode) model.getRoot(); if (root != null && root.getChildCount() > 0) { expandedGroupIds = new ArrayList<String>(); for (Enumeration<? extends MutableTreeTableNode> groupNodes = root.children(); groupNodes .hasMoreElements();) { AbstractChannelTableNode groupNode = (AbstractChannelTableNode) groupNodes.nextElement(); if (channelTable.isExpanded(new TreePath(new Object[] { root, groupNode })) || groupNode.getChildCount() == 0) { expandedGroupIds.add(groupNode.getGroupStatus().getGroup().getId()); } } } } return new TableState(selectedIds, expandedGroupIds); } private void restoreTableState(TableState tableState) { ChannelTreeTableModel model = (ChannelTreeTableModel) channelTable.getTreeTableModel(); MutableTreeTableNode root = (MutableTreeTableNode) model.getRoot(); if (model.isGroupModeEnabled()) { if (tableState.getExpandedGroupIds() != null && root != null) { channelTable.collapseAll(); for (Enumeration<? extends MutableTreeTableNode> groupNodes = root.children(); groupNodes .hasMoreElements();) { AbstractChannelTableNode groupNode = (AbstractChannelTableNode) groupNodes.nextElement(); if (tableState.getExpandedGroupIds().contains(groupNode.getGroupStatus().getGroup().getId())) { channelTable.expandPath(new TreePath(new Object[] { root, groupNode })); } } } else { channelTable.expandAll(); } } final List<TreePath> selectionPaths = new ArrayList<TreePath>(); for (Enumeration<? extends MutableTreeTableNode> children = root.children(); children.hasMoreElements();) { AbstractChannelTableNode child = (AbstractChannelTableNode) children.nextElement(); if (child.isGroupNode() && tableState.getSelectedIds().contains(child.getGroupStatus().getGroup().getId()) || !child.isGroupNode() && tableState.getSelectedIds() .contains(child.getChannelStatus().getChannel().getId())) { TreePath path = new TreePath(new Object[] { root, child }); channelTable.getTreeSelectionModel().addSelectionPath(path); selectionPaths.add(path); } if (model.isGroupModeEnabled()) { for (Enumeration<? extends MutableTreeTableNode> channelNodes = child.children(); channelNodes .hasMoreElements();) { AbstractChannelTableNode channelNode = (AbstractChannelTableNode) channelNodes.nextElement(); if (tableState.getSelectedIds().contains(channelNode.getChannelStatus().getChannel().getId())) { TreePath path = new TreePath(new Object[] { root, child, channelNode }); channelTable.getTreeSelectionModel().addSelectionPath(path); selectionPaths.add(path); } } } } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { for (TreePath path : selectionPaths) { channelTable.getTreeSelectionModel().addSelectionPath(path); } } }); } private class TableState { private List<String> selectedIds = new ArrayList<String>(); private List<String> expandedGroupIds = new ArrayList<String>(); public TableState(List<String> selectedIds, List<String> expandedGroupIds) { this.selectedIds = selectedIds; this.expandedGroupIds = expandedGroupIds; } public List<String> getSelectedIds() { return selectedIds; } public List<String> getExpandedGroupIds() { return expandedGroupIds; } } private class GroupDetailsDialog extends MirthDialog { private boolean saved = false; private boolean newGroup; public GroupDetailsDialog(boolean newGroup) { super(parent, true); this.newGroup = newGroup; initComponents(); initLayout(); if (newGroup) { String name; int index = 1; do { name = "Group " + index++; } while (!checkGroupName(name)); groupNameField.setText(name); groupNameField.requestFocus(); groupNameField.selectAll(); } else { AbstractChannelTableNode selectedNode = (AbstractChannelTableNode) channelTable .getPathForRow(channelTable.getSelectedRow()).getLastPathComponent(); groupNameField.setText(selectedNode.getGroupStatus().getGroup().getName()); groupDescriptionScrollPane.setText(selectedNode.getGroupStatus().getGroup().getDescription()); groupNameField.requestFocus(); groupNameField.setCaretPosition(groupNameField.getDocument().getLength()); } setPreferredSize(new Dimension(600, 375)); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setTitle("Channel Group Details"); pack(); setLocationRelativeTo(parent); setVisible(true); } public boolean wasSaved() { return saved; } public String getGroupName() { return groupNameField.getText(); } public String getGroupDescription() { return groupDescriptionScrollPane.getText(); } private void initComponents() { setBackground(UIConstants.BACKGROUND_COLOR); getContentPane().setBackground(getBackground()); containerPanel = new JPanel(); containerPanel.setBackground(getBackground()); containerPanel.setBorder(BorderFactory.createTitledBorder("Group Settings")); groupNameLabel = new JLabel("Name:"); groupNameField = new JTextField(); groupDescriptionLabel = new JLabel("Description:"); groupDescriptionScrollPane = new MirthRTextScrollPane(null, false, SyntaxConstants.SYNTAX_STYLE_NONE, false); groupDescriptionScrollPane.setSaveEnabled(false); separator = new JSeparator(SwingConstants.HORIZONTAL); okButton = new JButton("OK"); okButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { String name = groupNameField.getText(); if (StringUtils.isBlank(name)) { groupNameField.setBackground(UIConstants.INVALID_COLOR); parent.alertError(GroupDetailsDialog.this, "Group name cannot be blank."); return; } if (!checkGroupName(name, newGroup)) { groupNameField.setBackground(UIConstants.INVALID_COLOR); parent.alertError(GroupDetailsDialog.this, "Group name is already in use."); return; } saved = true; dispose(); } }); cancelButton = new JButton("Cancel"); cancelButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { dispose(); } }); } private void initLayout() { setLayout(new MigLayout("insets 8, novisualpadding, hidemode 3, fill")); containerPanel.setLayout(new MigLayout("insets 8, novisualpadding, hidemode 3, fill, gap 12 6")); containerPanel.add(groupNameLabel, "right"); containerPanel.add(groupNameField, "w 200!"); containerPanel.add(groupDescriptionLabel, "newline, top, right"); containerPanel.add(groupDescriptionScrollPane, "grow, push"); add(containerPanel, "grow, push"); add(separator, "newline, growx"); add(okButton, "newline, w 51!, right, split 2"); add(cancelButton, "w 51!"); } private JPanel containerPanel; private JLabel groupNameLabel; private JTextField groupNameField; private JLabel groupDescriptionLabel; private MirthRTextScrollPane groupDescriptionScrollPane; private JSeparator separator; private JButton okButton; private JButton cancelButton; } private class GroupAssignmentDialog extends MirthDialog { private boolean saved = false; public GroupAssignmentDialog() { super(parent, true); initComponents(); initLayout(); setPreferredSize(new Dimension(337, 118)); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setTitle("Channel Group Assignment"); pack(); setLocationRelativeTo(parent); setVisible(true); } public boolean wasSaved() { return saved; } public String getSelectedGroupId() { return ((Pair<String, String>) groupComboBox.getSelectedItem()).getLeft(); } private void initComponents() { setBackground(UIConstants.BACKGROUND_COLOR); getContentPane().setBackground(getBackground()); groupComboBox = new JComboBox<Pair<String, String>>(); List<Pair<String, String>> groups = new ArrayList<Pair<String, String>>(); for (Enumeration<? extends MutableTreeTableNode> groupNodes = ((MutableTreeTableNode) channelTable .getTreeTableModel().getRoot()).children(); groupNodes.hasMoreElements();) { ChannelGroup group = ((AbstractChannelTableNode) groupNodes.nextElement()).getGroupStatus() .getGroup(); groups.add(new MutablePair<String, String>(group.getId(), group.getName()) { @Override public String toString() { return getRight(); } }); } groupComboBox.setModel( new DefaultComboBoxModel<Pair<String, String>>(groups.toArray(new Pair[groups.size()]))); groupComboBox.setSelectedIndex(0); separator = new JSeparator(SwingConstants.HORIZONTAL); okButton = new JButton("OK"); okButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { saved = true; dispose(); } }); cancelButton = new JButton("Cancel"); cancelButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { dispose(); } }); } private void initLayout() { setLayout(new MigLayout("insets 8, novisualpadding, hidemode 3, fill")); add(new JLabel("Choose the group to assign the selected channel(s) to.")); add(groupComboBox, "newline, growx"); add(separator, "newline, growx"); add(okButton, "newline, w 51!, right, split 2"); add(cancelButton, "w 51!"); } private JComboBox<Pair<String, String>> groupComboBox; private JSeparator separator; private JButton okButton; private JButton cancelButton; } public JXTaskPane channelTasks; public JPopupMenu channelPopupMenu; public JXTaskPane groupTasks; public JPopupMenu groupPopupMenu; private JSplitPane splitPane; private JPanel topPanel; private MirthTreeTable channelTable; private JScrollPane channelScrollPane; private JPanel filterPanel; private JButton tagsFilterButton; private JLabel tagsLabel; private IconToggleButton tableModeGroupsButton; private IconToggleButton tableModeChannelsButton; private JTabbedPane tabPane; }