com.archivas.clienttools.arcmover.gui.HCPDataMigrator.java Source code

Java tutorial

Introduction

Here is the source code for com.archivas.clienttools.arcmover.gui.HCPDataMigrator.java

Source

// Copyright 2007 Hitachi Data Systems
// All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package com.archivas.clienttools.arcmover.gui;

import com.archivas.clienttools.arcmover.gui.namespacewizard.NamespaceProfileMgrDialog;
import com.archivas.clienttools.arcmover.gui.panels.ProfilePanel;
import com.archivas.clienttools.arcmover.gui.settings.PreferencesDialog;
import com.archivas.clienttools.arcmover.gui.ssl.SSLCertificateAllowDialog;
import com.archivas.clienttools.arcmover.gui.util.DialogDimensionProperties;
import com.archivas.clienttools.arcmover.gui.util.FixedWidthPanel;
import com.archivas.clienttools.arcmover.gui.util.GUIHelper;
import com.archivas.clienttools.arcmover.gui.util.SelfDestructiveDialog;
import com.archivas.clienttools.arcutils.api.jobs.CopyJob;
import com.archivas.clienttools.arcutils.api.jobs.DeleteJob;
import com.archivas.clienttools.arcutils.api.jobs.JobSpec;
import com.archivas.clienttools.arcutils.api.jobs.ManagedJob;
import com.archivas.clienttools.arcutils.api.jobs.SetMetadataJob;
import com.archivas.clienttools.arcutils.config.ConfigurationException;
import com.archivas.clienttools.arcutils.config.ConfigurationHelper;
import com.archivas.clienttools.arcutils.config.HCPMoverProperties;
import com.archivas.clienttools.arcutils.impl.adapter.StorageAdapterException;
import com.archivas.clienttools.arcutils.profile.AbstractProfileBase;
import com.archivas.clienttools.arcutils.profile.FileSystemProfile;
import com.archivas.clienttools.arcutils.profile.HCAPProfile;
import com.archivas.clienttools.arcutils.profile.ProfileManager;
import com.archivas.clienttools.arcutils.model.*;
import com.archivas.clienttools.arcutils.utils.FileListParser;
import com.archivas.clienttools.arcutils.utils.FileUtil;
import com.archivas.clienttools.arcutils.utils.UidGidUtil;
import com.archivas.clienttools.arcutils.utils.database.DBUtils;
import com.archivas.clienttools.arcutils.utils.database.DatabaseException;
import com.archivas.clienttools.arcutils.utils.database.DatabaseResourceManager;
import com.archivas.clienttools.arcutils.utils.database.NonFatalDatabaseException;
import com.archivas.clienttools.arcutils.utils.net.SSLCertChain;
import com.archivas.clienttools.arcutils.utils.net.SSLCertificateCallback;
import com.jgoodies.looks.plastic.PlasticLookAndFeel;
import com.jgoodies.looks.plastic.PlasticXPLookAndFeel;
import com.jgoodies.looks.plastic.theme.ExperienceBlue;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class HCPDataMigrator extends JFrame {

    private static final String CLASS_FULL_NAME = HCPDataMigrator.class.getName();
    private static final Logger LOG = Logger.getLogger(CLASS_FULL_NAME);

    // logging
    public static final String NEWLINE = System.getProperty("line.separator");

    public static final String PRODUCT_NAME = "Open Data Migrator for Hitachi Content Platform";

    // last selected profiles
    private static final String LEFT_PROFILE_NAME = "hcpdm.profile.left.name";
    private static final String RIGHT_PROFILE_NAME = "hcpdm.profile.right.name";

    private static final String FILE_CHOOSER_LOOK_IN_LABEL_TEXT_PROPERTY = "FileChooser.lookInLabelText";
    private static final String FILE_CHOOSER_FILE_NAME_LABEL_TEXT_PROPERTY = "FileChooser.fileNameLabelText";
    private static final String FILE_CHOOSER_FILES_OF_TYPE_LABEL_TEXT_PROPERTY = "FileChooser.filesOfTypeLabelText";

    // Copyright year for splash screen/about
    public static final int COPYRIGHT_YEAR = 2007;

    // GUI Support
    SplashDialog splashDialog;
    public static ImageIcon appImageIcon;
    private DialogDimensionProperties dialogProperties = new DialogDimensionProperties(CLASS_FULL_NAME, 700, 550);
    public static Cursor waitCursor = new Cursor(Cursor.WAIT_CURSOR);
    public static Cursor normalCursor = null;

    // GUI
    private static HCPDataMigrator mainFrame;

    private ProfilePanel leftProfilePanel;
    private ProfilePanel rightProfilePanel;
    protected JButton putButton;
    protected JButton getButton;

    protected JMenuItem openMenuItem = null;
    protected JMenuItem deleteMenuItem = null;
    protected JMenuItem setMetadataMenuItem = null;
    protected JMenuItem renameMenuItem = null;
    protected JMenuItem propertiesMenuItem = null;

    private JMenuItem toolbarMenuItem = null;
    private JToolBar toolBar;
    private JComboBox fontChooserCB;
    private JComboBox fontSizeCB;
    private static final Integer[] FONT_SIZES = { 8, 9, 10, 11, 12, 14 };

    private List<JMenuItem> lockableMenuItems = null;

    public static HCPDataMigrator getInstance() {
        return mainFrame;
    }

    public HCPDataMigrator(SplashDialog splash) throws StorageAdapterException {
        mainFrame = this;
        // Initialize the Splash Screen
        LOG.fine("Initializing Splash Screen");

        // set up icons first so that we can associate the app icon with the splash screen
        initializeIcons();
        splashDialog = splash;
        setTitle(PRODUCT_NAME);

        // Initialize Application
        LOG.fine("Initializing Application");

        // Initialize the GUI
        LOG.fine("Initializing GUI");

        ProfileManager.initialize(new SSLCertificateCallback() {

            public void validCertCallback(AbstractProfileBase profile, SSLCertChain certChain) {
                profile.setSSLCertChain(certChain);
                LOG.log(Level.FINE, "validCertCallback for profile " + profile + ": " + certChain);
            }

            public AllowSSLCert exceptionCallback(AbstractProfileBase profile, SSLCertChain certChain,
                    String warningString) {

                profile.setSSLCertChain(certChain);

                SSLCertificateAllowDialog sslCertificateAllowDialog = new SSLCertificateAllowDialog(profile);
                sslCertificateAllowDialog.setWarningText(warningString);
                sslCertificateAllowDialog.setTitle("Allow Security Exception");

                // center the dialog on the main window and display
                GUIHelper.centerDialog(HCPDataMigrator.getInstance(), sslCertificateAllowDialog);
                sslCertificateAllowDialog.setVisible(true);
                return sslCertificateAllowDialog.getAllowCert();
            }
        });

        layoutGuiComponents();
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        setSize(new Dimension(dialogProperties.width, dialogProperties.height));
        setLocationRelativeTo(null); // center on screen
        initializeMenuBar();
        initializeWindowListeners();
        initializeButtons();

        GUIHelper.invokeAndWait(new Runnable() {
            public void run() {
                selectionFromPanel(null);
            }
        }, "clear selection during initialization");

        LOG.fine("Initialization complete");
        this.setMinimumSize(new Dimension(650, 500));

        setCursor(waitCursor);
        setVisible(true);
        splashDialog.cleanUp();
        setCursor(normalCursor);
        loadProfilePanels();
    }

    private void layoutGuiComponents() {

        //
        // Buttons (left and right arrows)
        //
        getButton = new JButton();
        getButton.setIcon(new ImageIcon(getClass().getResource("/images/Rewind24.gif")));
        getButton.setText("");
        getButton.setToolTipText("Copy selected files from right to left.");
        putButton = new JButton();
        putButton.setIcon(new ImageIcon(getClass().getResource("/images/FastForward24.gif")));
        putButton.setText("");
        putButton.setToolTipText("Copy selected files from left to right.");
        JPanel moveButtonsPanel = new FixedWidthPanel();
        moveButtonsPanel.setLayout(new BoxLayout(moveButtonsPanel, BoxLayout.Y_AXIS));
        moveButtonsPanel.add(Box.createVerticalGlue());
        moveButtonsPanel.add(putButton);
        moveButtonsPanel.add(Box.createVerticalStrut(25));
        moveButtonsPanel.add(getButton);
        moveButtonsPanel.add(Box.createVerticalGlue());

        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
        p.setBorder(new EmptyBorder(12, 12, 12, 12));
        leftProfilePanel = new ProfilePanel();
        p.add(leftProfilePanel);
        p.add(Box.createHorizontalStrut(25));
        p.add(moveButtonsPanel);
        p.add(Box.createHorizontalStrut(25));
        rightProfilePanel = new ProfilePanel();
        p.add(rightProfilePanel);

        // Create the main panel that has the toolbar at start and everything else in the center
        initializeToolBar();
        JPanel mainPanel = new JPanel(new BorderLayout());
        mainPanel.add(toolBar, BorderLayout.NORTH);
        mainPanel.add(p, BorderLayout.CENTER);

        getContentPane().add(mainPanel);
    }

    private void initializeToolBar() {
        // determine all supported font families and add to combo box
        GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] allFonts = e.getAvailableFontFamilyNames();
        fontChooserCB = new JComboBox(allFonts);

        fontSizeCB = new JComboBox(FONT_SIZES);

        ActionListener l = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                setFont();
            }
        };
        fontChooserCB.addActionListener(l);
        fontSizeCB.addActionListener(l);

        // lay out the toolbar GUI components
        toolBar = new JToolBar(PRODUCT_NAME + " File List Font");
        toolBar.add(new JLabel("Font:"));
        toolBar.addSeparator();
        toolBar.add(fontChooserCB);
        fontChooserCB.setMaximumSize(fontChooserCB.getPreferredSize());
        toolBar.addSeparator();
        toolBar.add(fontSizeCB);
        fontSizeCB.setMaximumSize(fontSizeCB.getPreferredSize());
        toolBar.addSeparator();

        // Set toolbar to visible or not depending on last value
        toolBar.setVisible(HCPMoverProperties.TOOLBAR_VISIBLE.getAsBoolean());

        // set initial selection of font family and size to default font used
        Font initialFont = leftProfilePanel.getFileFont();
        // Now try to read in the user's preference
        try {
            String fontFamily = HCPMoverProperties.FONT_FAMILY.get();
            int fontSize = HCPMoverProperties.FONT_SIZE.getAsInt();
            fontSize = Math.max(fontSize, 8); // at least size 8
            fontSize = Math.min(fontSize, 14); // at most size 14

            // According to the API, if the fontFamily doesn't exist, then "Dialog" is used, i.e.
            // initialFont.getFamily() will return "Dialog"
            initialFont = new Font(fontFamily, Font.PLAIN, fontSize);
        } catch (Exception ignore) {
            // do nothing; we'll use the font that the L&F uses by default for profile panel from
            // above
        }
        fontChooserCB.setSelectedItem(initialFont.getFamily());
        fontSizeCB.setSelectedItem(initialFont.getSize());

        // Now tell profile panels to use the correct initial font
        setFont();
    }

    private void setFont() {
        String newFontName = (String) fontChooserCB.getSelectedItem();
        Integer newFontSize = (Integer) fontSizeCB.getSelectedItem();
        if (newFontName != null && newFontSize != null) {
            Font fileFont = new Font(newFontName, Font.PLAIN, newFontSize);
            HCPMoverProperties.FONT_FAMILY.set(fileFont.getFamily());
            HCPMoverProperties.FONT_SIZE.set(fileFont.getSize());
            leftProfilePanel.setFileFont(fileFont);
            rightProfilePanel.setFileFont(fileFont);
            JobDialog.setFileFont(fileFont);
        }
    }

    private void initializeIcons() {
        URL appImageIconURL = HCPDataMigrator.class.getResource("/images/DataMigratorIcon16x16.png");
        appImageIcon = new ImageIcon(appImageIconURL);

        setIconImage(appImageIcon.getImage());
    }

    private void initializeButtons() {

        putButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    copyLeftToRight();
                } catch (Exception ex) {
                    String msg = DBUtils.getErrorMessage("An error occurred copying files", ex);
                    GUIHelper.showMessageDialog(HCPDataMigrator.this, msg, "Error Copying Files",
                            JOptionPane.ERROR_MESSAGE);
                    LOG.log(Level.WARNING, msg, ex);
                }
            }
        });

        getButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    copyRightToLeft();
                } catch (Exception ex) {
                    String msg = DBUtils.getErrorMessage("An error occurred copying files", ex);
                    GUIHelper.showMessageDialog(HCPDataMigrator.this, msg, "Error Copying Files",
                            JOptionPane.ERROR_MESSAGE);
                    LOG.log(Level.WARNING, msg, ex);
                }
            }
        });
    }

    private void initializeMenuBar() throws StorageAdapterException {
        JMenuBar menuBar;
        JMenu menu;
        JMenuItem menuItem;

        // create menu bar
        menuBar = new JMenuBar();

        // create File menu
        menu = new JMenu("File");
        menu.setMnemonic(KeyEvent.VK_F);
        menu.getAccessibleContext().setAccessibleDescription("File menu");
        menuBar.add(menu);

        // Form list of lockable items
        lockableMenuItems = new ArrayList<JMenuItem>();

        // create File menu items
        menuItem = new JMenuItem("Saved Jobs", KeyEvent.VK_S);
        menuItem.getAccessibleContext().setAccessibleDescription("Manage saved jobs");
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    savedJobsMenuItemAction();
                } catch (Exception ex) {
                    String errMsg = DBUtils.getErrorMessage("An error occurred getting the list of saved jobs", ex);
                    LOG.log(Level.SEVERE, errMsg, ex);
                    GUIHelper.showMessageDialog(HCPDataMigrator.this, errMsg, "Error Reading Jobs",
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        });
        menu.add(menuItem);
        lockableMenuItems.add(menuItem);

        // create File menu items
        menuItem = new JMenuItem("Import Copy Job from File", KeyEvent.VK_C);
        menuItem.getAccessibleContext().setAccessibleDescription("Load a Copy Job File");
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                loadCopyJobMenuItemAction();
            }
        });
        menu.add(menuItem);
        lockableMenuItems.add(menuItem);

        menuItem = new JMenuItem("Import Delete Job from File", KeyEvent.VK_I);
        menuItem.getAccessibleContext().setAccessibleDescription("Load a Delete Job File");
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                loadDeleteJobMenuItemAction();
            }
        });
        menu.add(menuItem);
        lockableMenuItems.add(menuItem);

        menuItem = new JMenuItem("Import Metadata Job from File", KeyEvent.VK_M);
        menuItem.getAccessibleContext().setAccessibleDescription("Load a Metadata Job File");
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                loadMetadataJobMenuItemAction();
            }
        });
        menu.add(menuItem);
        lockableMenuItems.add(menuItem);

        menu.addSeparator();

        menuItem = new JMenuItem("Namespace Profile Manager", KeyEvent.VK_N);
        menuItem.getAccessibleContext().setAccessibleDescription("Open the Namespace Profile Manager");
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                loadProfileManagerMenuItemAction();
            }
        });
        menu.add(menuItem);
        lockableMenuItems.add(menuItem);

        menu.addSeparator();

        openMenuItem = new JMenuItem("Open", KeyEvent.VK_O);
        openMenuItem.setEnabled(false);
        openMenuItem.getAccessibleContext().setAccessibleDescription("Open Object");
        openMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                openObjectAction();
            }
        });
        menu.add(openMenuItem);

        deleteMenuItem = new JMenuItem("Delete", KeyEvent.VK_D);
        deleteMenuItem.setEnabled(false);
        deleteMenuItem.getAccessibleContext().setAccessibleDescription("Delete Object");
        deleteMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                deleteObjectAction();
            }
        });
        menu.add(deleteMenuItem);

        setMetadataMenuItem = new JMenuItem("Set Metadata" /*
                                                            * , KeyEvent.VK_E // no available
                                                            * reasonable letter
                                                            */);
        setMetadataMenuItem.setEnabled(false);
        setMetadataMenuItem.getAccessibleContext().setAccessibleDescription("Set Metadata");
        setMetadataMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                setMetadataObjectAction();
            }
        });
        menu.add(setMetadataMenuItem);

        renameMenuItem = new JMenuItem("Rename", KeyEvent.VK_R);
        renameMenuItem.setEnabled(false);
        renameMenuItem.getAccessibleContext().setAccessibleDescription("Rename Object");
        renameMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                renameObjectAction();
            }
        });
        menu.add(renameMenuItem);

        propertiesMenuItem = new JMenuItem("Properties", KeyEvent.VK_P);
        propertiesMenuItem.setMnemonic(KeyEvent.VK_P);
        propertiesMenuItem.setEnabled(false);
        propertiesMenuItem.getAccessibleContext().setAccessibleDescription("Object Properties");
        propertiesMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                objectPropertiesAction();
            }
        });
        menu.add(propertiesMenuItem);

        menu.addSeparator();

        menuItem = new JMenuItem("Exit", KeyEvent.VK_X);
        menuItem.setMnemonic(KeyEvent.VK_X);
        menuItem.getAccessibleContext().setAccessibleDescription("Exit this application");
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                quit();
            }
        });
        menu.add(menuItem);

        // create Edit menu
        menu = new JMenu("Edit");
        menu.setMnemonic(KeyEvent.VK_E);
        menu.getAccessibleContext().setAccessibleDescription("Edit menu");
        menuBar.add(menu);

        // create Edit menu items
        menuItem = new JMenuItem("Preferences", KeyEvent.VK_P);
        menuItem.getAccessibleContext().setAccessibleDescription("Application preferences");
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                preferencesMenuItemAction();
            }
        });
        menu.add(menuItem);

        // create View menu
        menu = new JMenu("View");
        menu.setMnemonic(KeyEvent.VK_V);
        menu.getAccessibleContext().setAccessibleDescription("View menu");
        menuBar.add(menu);

        // create View menu items
        menuItem = new JMenuItem("Refresh");
        menuItem.setMnemonic(KeyEvent.VK_R);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0));
        menuItem.getAccessibleContext().setAccessibleDescription("Refresh view");
        menuItem.setIcon(GUIHelper.REFRESH_ICON);
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshPanels();
                JobDialog.showJob();
            }
        });
        menu.add(menuItem);

        toolbarMenuItem = new JCheckBoxMenuItem("Font Toolbar");
        toolbarMenuItem.getAccessibleContext().setAccessibleDescription("Display or Hide the Font Toolbar");
        toolbarMenuItem.setMnemonic(KeyEvent.VK_T);
        toolbarMenuItem.setSelected(HCPMoverProperties.TOOLBAR_VISIBLE.getAsBoolean());
        toolbarMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                toolBar.setVisible(toolbarMenuItem.isSelected());
                HCPMoverProperties.TOOLBAR_VISIBLE.set(toolbarMenuItem.isSelected());
            }
        });
        menu.add(toolbarMenuItem);

        // create Help menu
        menu = new JMenu("Help");
        menu.setMnemonic(KeyEvent.VK_H);
        menu.getAccessibleContext().setAccessibleDescription("Help menu");
        menuBar.add(menu);

        // create Help menu items
        menuItem = new JMenuItem("About", KeyEvent.VK_A);
        menuItem.getAccessibleContext().setAccessibleDescription("About this application");
        menuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                aboutMenuItemAction();
            }
        });
        menu.add(menuItem);

        // add menu bar to window
        setJMenuBar(menuBar);
    }

    private void quit() {
        // Veto with error message if JobDialog is open
        if (JobDialog.isOpen()) {
            LOG.log(Level.INFO, "Trying to close with open Job Dialog.");
            showMigrator(true);
            JOptionPane.showMessageDialog(HCPDataMigrator.this,
                    "The current job must be closed before closing the application.", "Error Closing Application",
                    JOptionPane.ERROR_MESSAGE);
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    JobDialog.showJob();
                }
            });
        } else {
            // remember size
            Dimension dimension = getSize();

            dialogProperties.width = dimension.width;
            dialogProperties.height = dimension.height;

            dialogProperties.persist();

            HCPMoverProperties.persist();

            try {
                ProfileManager.saveProfiles();
            } catch (ConfigurationException f) {
                LOG.log(Level.WARNING, "Failed to save profiles on exit.", f);
            }

            shutdown();
        }
    }

    private void initializeWindowListeners() {
        // add a hook to persist window dimensions on close
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                quit();
            }

            public void windowDeiconified(WindowEvent e) {
                toFront();
                requestFocus();
            }
        });

        addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent evt) {
                if (leftProfilePanel != null) {
                    leftProfilePanel.resizeFileList();
                }
                if (rightProfilePanel != null) {
                    rightProfilePanel.resizeFileList();
                }
            }
        });

        PropertyChangeListener listener = new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent e) {
                if (!SwingUtilities.isEventDispatchThread()) {
                    String errMsg = "propertyChange is not on the EDT but it should be";
                    IllegalStateException ex = new IllegalStateException(errMsg);
                    LOG.log(Level.SEVERE, errMsg, ex);
                    throw ex;
                }
                updateLockable();
            }
        };
        leftProfilePanel.addPropertyChangeListener(ProfilePanel.LOCK_PROPERTY, listener);
        rightProfilePanel.addPropertyChangeListener(ProfilePanel.LOCK_PROPERTY, listener);
    }

    public void updateLockable() {
        updateButtons();
        updateFileMenu();
    }

    public void refreshProfilePanel(AbstractProfileBase profile) {
        try {
            setCursor(waitCursor);
            if (leftProfilePanel.getSelectedProfile() == profile) {
                leftProfilePanel.refreshPathAndFileList();
            }
            if (rightProfilePanel.getSelectedProfile() == profile) {
                rightProfilePanel.refreshPathAndFileList();
            }
        } finally {
            setCursor(normalCursor);
        }
    }

    public void refreshProfiles() {
        try {
            setCursor(waitCursor);
            leftProfilePanel.refreshProfiles();
            rightProfilePanel.refreshProfiles();
        } finally {
            setCursor(normalCursor);
            repaint(); // repaint, otherwise the profile drop down doesn't display the selected
                       // profile
        }
    }

    private void loadProfilePanels() {
        try {
            setCursor(waitCursor);

            AbstractProfileBase profile = null;
            String profileName = ConfigurationHelper.getStringProperty(LEFT_PROFILE_NAME, null);
            if (profileName != null && ProfileManager.doesProfileNameExist(profileName)) {
                profile = ProfileManager.getProfileByName(profileName);
            }
            if (profile == null) {
                profile = FileSystemProfile.LOCAL_FILESYSTEM_PROFILE;
            }
            final AbstractProfileBase leftProfile = profile;
            GUIHelper.invokeAndWait(new Runnable() {
                public void run() {
                    leftProfilePanel.setSelectedProfile(leftProfile);
                }
            }, "set left profile");

            profile = null;
            profileName = ConfigurationHelper.getStringProperty(RIGHT_PROFILE_NAME, null);
            if (profileName != null && ProfileManager.doesProfileNameExist(profileName)) {
                profile = ProfileManager.getProfileByName(profileName);
            }
            if (profile == null) {
                profile = FileSystemProfile.LOCAL_FILESYSTEM_PROFILE;
            }
            final AbstractProfileBase rightProfile = profile;
            GUIHelper.invokeAndWait(new Runnable() {
                public void run() {
                    rightProfilePanel.setSelectedProfile(rightProfile);
                }
            }, "set right profile");
        } finally {
            setCursor(normalCursor);
            repaint(); // repaint, otherwise the profile drop down doesn't display the selected
                       // profile
        }
    }

    public void loadCopyJobMenuItemAction() {
        startImportJob(new ImportCopyJobDialog());
    }

    public void loadDeleteJobMenuItemAction() {
        startImportJob(new ImportDeleteJobDialog());
    }

    public void loadMetadataJobMenuItemAction() {
        startImportJob(new ImportMetadataJobDialog());
    }

    public void startImportJob(ImportJobDialog importDialog) {

        // setVisible(true) won't return until the dialog is closed
        importDialog.setVisible(true);

        if (importDialog.okClicked()) {
            String file = importDialog.getImportFilePath();
            AbstractProfileBase sourceProfile = importDialog.getSourceProfile();
            String sourcePath = sourceProfile.encode(importDialog.getSourcePath());
            AbstractProfileBase targetProfile = null;
            String targetPath = "";
            ManagedJob.Type jobType = importDialog.getJobType();
            if (jobType.hasTarget()) {
                targetProfile = importDialog.getTargetProfile();
                targetPath = targetProfile.encode(importDialog.getTargetPath());
            }

            this.getGlassPane().setCursor(waitCursor);
            this.getGlassPane().setVisible(true);

            // Create a job
            File inputFile = new File(file);
            ManagedJob job;
            switch (jobType) {
            case DELETE:
                job = new DeleteJob(sourceProfile, inputFile);
                break;
            case SET_METADATA:
                job = new SetMetadataJob(sourceProfile, inputFile);
                break;
            case COPY:
                job = new CopyJob(sourceProfile, targetProfile, inputFile);
                job.setTargetPath(targetPath);
                break;
            default:
                throw new IllegalArgumentException(jobType.toString());
            }
            job.setSourcePath(sourcePath);
            LoadSchedule schedule = LoadSchedule.getDefaultLoadSchedule();
            job.setLoadSchedule(schedule);

            try {
                // Validate the file
                FileListParser.validateFile(inputFile, sourceProfile, sourcePath, targetPath);
            } catch (Exception e) {
                String msg = DBUtils.getErrorMessage("An error occurred parsing file " + file, e);
                LOG.log(Level.WARNING, msg, e);
                GUIHelper.showMessageDialog(this, msg, "Error Reading Input File", JOptionPane.ERROR_MESSAGE);
                job = null;
            }

            this.getGlassPane().setCursor(null);
            this.getGlassPane().setVisible(false);

            try {
                if (job != null) {
                    JobDialog.openNewJob(job);
                }
            } catch (Exception e) {
                String msg = DBUtils.getErrorMessage("An error occurred loading a new job", e);
                LOG.log(Level.WARNING, msg, e);
                GUIHelper.showMessageDialog(this, msg, "Error Loading Job", JOptionPane.ERROR_MESSAGE);
            }

            HCPMoverProperties.IMPORT_JOB_LAST_DIRECTORY.set(file);
        }
    }

    private void savedJobsMenuItemAction() throws DatabaseException {
        SavedJobsDialog.openSavedJobsDialog();
    }

    private void loadProfileManagerMenuItemAction() {
        new NamespaceProfileMgrDialog().setVisible(true);
    }

    private void aboutMenuItemAction() {
        new AboutDialog(this, "3.0.14.12345");
    }

    private void preferencesMenuItemAction() {
        PreferencesDialog d = new PreferencesDialog();
        d.setLocationRelativeTo(this);
        d.setVisible(true);
    }

    private JobSpec getCurrentJob(ProfilePanel sourcePanel, ProfilePanel targetPanel) throws DatabaseException {
        AbstractProfileBase sourceProfile = sourcePanel.getSelectedProfile();
        AbstractProfileBase targetProfile = targetPanel.getSelectedProfile();
        ArcMoverFile[] selectedFiles = sourcePanel.getSelectedFiles();

        List<ArcCopyFile> fileNameList = new ArrayList<ArcCopyFile>();
        String targetPath = targetPanel.getCurrentDirectory().getPath();

        // add each selected file to the list
        for (ArcMoverFile nextFile : selectedFiles) {
            String targetFilename = nextFile.getFileName();
            //
            // encode/decode the target filename as necessary
            //
            if (sourceProfile.usesEncodedPaths() && !targetProfile.usesEncodedPaths()) {
                targetFilename = sourceProfile.decode(targetFilename);
            } else if (!sourceProfile.usesEncodedPaths() && targetProfile.usesEncodedPaths()) {
                targetFilename = targetProfile.encode(targetFilename);
            }
            ArcCopyFile nextCopyFile = new ArcCopyFile(nextFile, targetPanel.getSelectedProfile(), FileUtil
                    .resolvePath(targetPath, targetFilename, targetPanel.getSelectedProfile().getPathSeparator()));
            fileNameList.add(nextCopyFile);
        }

        CopyJob job = new CopyJob(sourcePanel.getSelectedProfile(), targetPanel.getSelectedProfile(), fileNameList);
        job.setSourcePath(sourcePanel.getCurrentDirectory().getPath());
        job.setTargetPath(targetPath);
        return job;
    }

    public CopyJob configureJob(CopyJob job) throws DatabaseException {
        // The following copies the values from our preferences. We do not use this in the
        // case where the source is HCP. Filtering that case out here
        if (!(job.getSourceProfile() instanceof HCAPProfile)) {
            if (HCPMoverProperties.PERMISSIONS_UID.get().length() > 0) {
                try {
                    job.setUid(UidGidUtil.validateId(HCPMoverProperties.PERMISSIONS_UID.get()));
                } catch (NumberFormatException exception) {
                    LOG.warning("Ignoring invalid UID value: " + HCPMoverProperties.PERMISSIONS_UID.get());
                }
            }

            if (HCPMoverProperties.PERMISSIONS_GID.get().length() > 0) {
                try {
                    job.setGid(UidGidUtil.validateId(HCPMoverProperties.PERMISSIONS_GID.get()));
                } catch (NumberFormatException exception) {
                    LOG.warning("Ignoring invalid GID value: " + HCPMoverProperties.PERMISSIONS_GID.get());
                }
            }

            if (HCPMoverProperties.PERMISSIONS_FILE_MODE.get().length() > 0) {
                try {
                    if (HCPMoverProperties.PERMISSIONS_FILE_MODE.getAsInt() >= 0)
                        job.setFilePermissions(HCPMoverProperties.PERMISSIONS_FILE_MODE.getAsInt());

                } catch (NumberFormatException exception) {
                    LOG.warning(
                            "Ignoring invalid file mode value: " + HCPMoverProperties.PERMISSIONS_FILE_MODE.get());
                }
            }

            if (HCPMoverProperties.PERMISSIONS_DIR_MODE.get().length() > 0) {
                try {
                    if (HCPMoverProperties.PERMISSIONS_DIR_MODE.getAsInt() >= 0)
                        job.setDirPermissions(HCPMoverProperties.PERMISSIONS_DIR_MODE.getAsInt());
                } catch (NumberFormatException exception) {
                    LOG.warning(
                            "Ignoring invalid dir mode value: " + HCPMoverProperties.PERMISSIONS_DIR_MODE.get());
                }
            }

            job.setIndex(HCPMoverProperties.INDEX_MODE.getAsTriState().toBoolean());
            job.setShred(HCPMoverProperties.SHRED_MODE.getAsTriState().toBoolean());
            job.setRetentionHold(HCPMoverProperties.RETENTION_HOLD_MODE.getAsTriState().toBoolean());

            // check if an explicit retention period is being provided
            job.setRetention(Retention.fromProperties(
                    Retention.Type.fromUIName(HCPMoverProperties.RETENTION_TYPE.get()),
                    HCPMoverProperties.RETENTION_USER_INPUT.get(), HCPMoverProperties.RETENTION_HCAP_VALUE.get()));

            if (HCPMoverProperties.OWNER.get().length() > 0) {
                job.setOwner(Owner.createFromStringRepresentation(HCPMoverProperties.OWNER.get()));
            }
        }

        return job;
    }

    private void copyLeftToRight() throws DatabaseException {
        copyFiles(leftProfilePanel, rightProfilePanel);
    }

    private void copyRightToLeft() throws DatabaseException {
        copyFiles(rightProfilePanel, leftProfilePanel);
    }

    public void copyFiles(AbstractProfileBase sourceProfile, ProfilePanel targetPanel, List<ArcCopyFile> fileList)
            throws DatabaseException {
        CopyJob job = new CopyJob(sourceProfile, targetPanel.getSelectedProfile(), fileList);
        configureJob(job);
        copyFiles(job, fileList.toArray());
        targetPanel.refreshPathAndFileList();
    }

    public void copyFiles(ProfilePanel sourcePanel, ProfilePanel targetPanel) throws DatabaseException {
        CopyJob job = (CopyJob) getCurrentJob(sourcePanel, targetPanel);
        configureJob(job);
        copyFiles(job, sourcePanel.getSelectedFiles());
    }

    public void copyFiles(CopyJob job, Object[] selectedObjectArray) {
        try {
            // make sure a local file has been selected
            if (selectedObjectArray.length > 0) {
                JobDialog.openNewJob(job);
            }
        } catch (JobAlreadyOpenException jaoe) {
            JOptionPane.showMessageDialog(this, "Another job is already open.\nOnly one job may be open at a time.",
                    "Error", JOptionPane.ERROR_MESSAGE);
        } catch (Exception e1) {
            String msg = DBUtils.getErrorMessage("An unexpected error occurred", e1);
            LOG.log(Level.WARNING, msg, e1);
            GUIHelper.showMessageDialog(this, msg, "Error", JOptionPane.ERROR_MESSAGE);
        } finally {
            setCursor(normalCursor);
        }

    }

    public static void main(String[] args) {
        try {
            for (Map.Entry<Object, Object> prop : System.getProperties().entrySet()) {
                LOG.log(Level.INFO, "System Property: " + prop.getKey() + "=" + prop.getValue());
            }

            PlasticXPLookAndFeel.setPlasticTheme(new ExperienceBlue());
            PlasticLookAndFeel.setPlasticTheme(new ExperienceBlue());

            UIManager.setLookAndFeel(new PlasticXPLookAndFeel());

            // Change label texts per UI bug #21966
            UIManager.put(FILE_CHOOSER_LOOK_IN_LABEL_TEXT_PROPERTY,
                    UIManager.getString(FILE_CHOOSER_LOOK_IN_LABEL_TEXT_PROPERTY).replace(":", ""));
            UIManager.put(FILE_CHOOSER_FILE_NAME_LABEL_TEXT_PROPERTY,
                    UIManager.getString(FILE_CHOOSER_FILE_NAME_LABEL_TEXT_PROPERTY).replace(":", ""));
            UIManager.put(FILE_CHOOSER_FILES_OF_TYPE_LABEL_TEXT_PROPERTY,
                    UIManager.getString(FILE_CHOOSER_FILES_OF_TYPE_LABEL_TEXT_PROPERTY).replace(":", ""));

        } catch (Exception e) {
            LOG.warning("" + e);
            throw new RuntimeException(e);
        }

        SplashDialog splash = new SplashDialog();
        try {
            validateLaunchOK(splash);
            upgrade(splash);
            new HCPDataMigrator(splash);
        } catch (StorageAdapterException e) {
            LOG.warning("" + e);
            throw new RuntimeException(e);
        }
    }

    private static void upgrade(final SplashDialog splashDialog) {
        final String failureDialogTitle = PRODUCT_NAME + " Fatal Error";
        try {
            int compare = DatabaseResourceManager.compareCurrentVersionToDbVersion();
            if (compare == 0) {
                // versions are the same; no action required
            } else if (compare < 0) {
                // database version later than code version; can't run
                String msg = String.format(
                        "The %1$s database has already been upgraded to a more recent version than is supported by this version of %1$s.  Please use a more recent version of %1$s.",
                        PRODUCT_NAME);
                LOG.log(Level.SEVERE, msg);
                GUIHelper.showMessageDialog(splashDialog, msg, failureDialogTitle, JOptionPane.ERROR_MESSAGE);
                System.exit(-1);
            } else {
                // the database needs upgrading
                splashDialog.setAlwaysOnTop(false); // splashDialog prevents pleaseWaitDialog from
                                                    // displaying if it is set to always on top
                final NoUserControlMessageDialog pleaseWaitDialog = new NoUserControlMessageDialog(splashDialog,
                        String.format("Upgrading %s Database", PRODUCT_NAME),
                        String.format("Upgrading the %s database, please wait.", PRODUCT_NAME));
                pleaseWaitDialog.setAlwaysOnTop(true);

                SwingWorker sw = new SwingWorker() {
                    private String finishMsg = String.format("Completed upgrading the %s database", PRODUCT_NAME);
                    private String finishTitle = String.format("%s Upgrade Complete", PRODUCT_NAME);
                    private boolean isError = false;
                    private int msgDisplayMs = 5000;

                    protected Object doInBackground() throws Exception {
                        try {
                            do {
                                try {
                                    Thread.sleep(1000);
                                } catch (Exception ignore) {
                                } // wait 1 second for wait message to display
                            } while (!pleaseWaitDialog.isVisible());
                            DatabaseResourceManager.upgradeDatabase();
                        } catch (NonFatalDatabaseException e) {
                            finishMsg = DBUtils.getErrorMessage(String
                                    .format("A partial error occurred upgrading the %s database", PRODUCT_NAME), e);
                            finishMsg += "\n\nOne or more saved jobs may not be usable.";
                            msgDisplayMs = -1; // display until user clicks ok
                            LOG.log(Level.WARNING, finishMsg, e);
                        } catch (Exception e) {
                            finishMsg = DBUtils.getErrorMessage(
                                    String.format("A fatal error occurred upgrading the %s database", PRODUCT_NAME),
                                    e);
                            finishMsg += "\n\nDelete the database to use this version of " + PRODUCT_NAME;
                            finishTitle = failureDialogTitle;
                            isError = true;
                            LOG.log(Level.SEVERE, finishMsg, e);
                        }
                        return null;
                    }

                    protected void done() {
                        if (isError) {
                            GUIHelper.showMessageDialog(splashDialog, finishMsg, finishTitle,
                                    JOptionPane.ERROR_MESSAGE);
                            System.exit(-1);
                        } else {
                            // we must display the "upgrade complete" dialog on top of the "upgrade
                            // in progress" dialog.
                            // If we call pleaseWaitDialog.setVisible(false) prior to the
                            // GUIHelper.showMessageDialog() completing, then
                            // the call to pleaseWaitDialog.setVisible(true) below completes before
                            // the GUIHelper.showMessageDialog() completes,
                            // and therefore we set splash dialog on top and return from this method
                            // prior to the "upgrade completed" dialog going away,
                            // which is not what we want.
                            GUIHelper.showMessageDialog(msgDisplayMs, pleaseWaitDialog, true, finishMsg,
                                    finishTitle, JOptionPane.INFORMATION_MESSAGE);
                            pleaseWaitDialog.setVisible(false);
                        }
                    }
                };
                sw.execute();
                pleaseWaitDialog.setVisible(true); // this will not return until the dialog is no
                                                   // longer visible
                splashDialog.setAlwaysOnTop(true);
            }
        } catch (Exception e) {
            String msg = "A fatal upgrade error occurred: " + e.getMessage();
            LOG.log(Level.SEVERE, msg, e);
            GUIHelper.showMessageDialog(splashDialog, msg, failureDialogTitle, JOptionPane.ERROR_MESSAGE);
            System.exit(-1);
        }
    }

    private static void validateLaunchOK(final SplashDialog splashDialog) {
        final String failureDialogTitle = PRODUCT_NAME + " Fatal Error";
        try {
            ConfigurationHelper.validateLaunchOK(false); // don't do upgrade here; we'll do it
                                                         // separately
        } catch (Exception e) {
            String msg = "A fatal error occurred: " + e.getMessage();
            LOG.log(Level.SEVERE, msg, e);
            GUIHelper.showMessageDialog(splashDialog, msg, failureDialogTitle, JOptionPane.ERROR_MESSAGE);
            System.exit(-1);
        }
    }

    private void shutdown() {
        DatabaseResourceManager.shutdownDB();
        System.exit(0);
    }

    protected void openObjectAction() {
        ProfilePanel panel = getActivePanel();
        if (panel != null && doesFilePanelHaveSingleSelection(panel)) {
            panel.openSelectedRow();
        }
    }

    protected void deleteObjectAction() {
        ProfilePanel panel = getActivePanel();
        if (panel != null && doesFilePanelHaveSelection(panel)) {
            panel.deleteSelectedRows();
        }
    }

    protected void setMetadataObjectAction() {
        ProfilePanel panel = getActivePanel();
        if (panel != null && doesFilePanelHaveSelection(panel)) {
            panel.setMetadataOnSelectedRows();
        }
    }

    protected void renameObjectAction() {
        ProfilePanel panel = getActivePanel();
        if (panel != null && doesFilePanelHaveSingleSelection(panel)) {
            panel.renameSelectedRow();
        }
    }

    protected void objectPropertiesAction() {
        ProfilePanel panel = getActivePanel();
        if (panel != null && doesFilePanelHaveSingleSelection(panel)) {
            panel.displaySelectedObjectProperties();
        }
    }

    public void selectionFromPanel(final ProfilePanel panelWithSelection) {
        // Enable/Disable buttons and menu options
        if (rightProfilePanel != panelWithSelection) {
            rightProfilePanel.clearSelection();
        }
        if (leftProfilePanel != panelWithSelection) {
            leftProfilePanel.clearSelection();
        }
        updateLockable();
    }

    /**
     * Updates the put and get buttons The put button is enabled if neither panel is locked, the
     * left side has a selection of at least one non-deleted file and the right side is not read
     * only and there is no open job The get button is enabled if neither panel is locked, the right
     * side has a selection of at least one non-deleted file and the left side is not read only and
     * there is no open job
     */
    private void updateButtons() {
        boolean locked = leftProfilePanel.isLocked() || rightProfilePanel.isLocked() || JobDialog.isOpen();
        boolean leftSelection = leftProfilePanel.getNonDeletedSelectedFileCount() > 0;
        putButton.setEnabled(!locked && leftSelection && !rightProfilePanel.isSelectionReadOnly());
        boolean rightSelection = rightProfilePanel.getNonDeletedSelectedFileCount() > 0;
        getButton.setEnabled(!locked && rightSelection && !leftProfilePanel.isSelectionReadOnly());
    }

    public void saveSelectedProfile(ProfilePanel panel, String profileName) {
        try {
            if (panel == leftProfilePanel) {
                ConfigurationHelper.setUserProperty(LEFT_PROFILE_NAME, profileName);
            } else if (panel == rightProfilePanel) {
                ConfigurationHelper.setUserProperty(RIGHT_PROFILE_NAME, profileName);
            }
        } catch (ConfigurationException e) {
            LOG.log(Level.WARNING, "Could not save selected profile", e);
        }
    }

    /**
     * Updates the file menu Each item is enabled based on the active panel, the current selected
     * file and whether there are one or more selected files Each lockable menu item is locked if
     * either panel is locked or there is an open job dialog
     */
    private void updateFileMenu() {
        if (!SwingUtilities.isEventDispatchThread()) {
            String errMsg = "updateFileMenu is not on the EDT but it should be";
            IllegalStateException ex = new IllegalStateException(errMsg);
            LOG.log(Level.SEVERE, errMsg, ex);
            throw ex;
        }
        ProfilePanel panel = getActivePanel();
        ArcMoverFile selectedFile = null;
        if (panel != null) {
            selectedFile = panel.getSelectedFile();
        }
        boolean single = doesFilePanelHaveSingleSelection(panel);
        setOpenMenuItemEnabled(panel, selectedFile, single);
        setDeleteMenuItemEnabled(panel, selectedFile, single);
        setSetMetadataMenuItemEnabled(panel, selectedFile, single);
        setRenameMenuItemEnabled(panel, selectedFile, single);
        setPropertiesMenuItemEnabled(panel, selectedFile, single);

        boolean locked = leftProfilePanel.isLocked() || rightProfilePanel.isLocked() || JobDialog.isOpen();
        for (JMenuItem item : lockableMenuItems) {
            item.setEnabled(!locked);
        }
    }

    private ProfilePanel getActivePanel() {
        ProfilePanel activePanel = null;
        if (leftProfilePanel.getSelectedRowCount() > 0) {
            activePanel = leftProfilePanel;
        } else if (rightProfilePanel.getSelectedRowCount() > 0) {
            activePanel = rightProfilePanel;
        }
        return activePanel;
    }

    protected void setOpenMenuItemEnabled(ProfilePanel panel, ArcMoverFile selectedFile, boolean single) {
        openMenuItem
                .setEnabled(panel != null && !panel.isLocked() && panel.supportsOpenAction(selectedFile, single));
    }

    /**
     * Sets the delete menu item Active if there is an active panel, it is unlocked, it supports
     * delete and the job dialog is not open
     * 
     * @param panel
     *            Current active panel
     * @param selectedFile
     *            Current selected file
     * @param single
     *            Whether there is one or more selected files
     */
    protected void setDeleteMenuItemEnabled(ProfilePanel panel, ArcMoverFile selectedFile, boolean single) {
        deleteMenuItem.setEnabled(!JobDialog.isOpen() && panel != null && !panel.isLocked()
                && panel.supportsDeleteAction(selectedFile, single));
    }

    /**
     * Sets the delete menu item Active if there is an active panel, it is unlocked, it supports
     * delete and the job dialog is not open
     * 
     * @param panel
     *            Current active panel
     * @param selectedFile
     *            Current selected file
     * @param single
     *            Whether there is one or more selected files
     */
    protected void setSetMetadataMenuItemEnabled(ProfilePanel panel, ArcMoverFile selectedFile, boolean single) {
        setMetadataMenuItem.setEnabled(!JobDialog.isOpen() && panel != null && !panel.isLocked()
                && panel.supportsSetMetadataAction(selectedFile, single));
    }

    /**
     * Sets the rename menu item Active if there is an active panel, it is unlocked, it supports
     * rename and the job dialog is not open
     * 
     * @param panel
     *            Current active panel
     * @param selectedFile
     *            Current selected file
     * @param single
     *            Whether there is one or more selected files
     */
    protected void setRenameMenuItemEnabled(ProfilePanel panel, ArcMoverFile selectedFile, boolean single) {
        renameMenuItem.setEnabled(!JobDialog.isOpen() && panel != null && !panel.isLocked()
                && panel.supportsRenameAction(selectedFile, single));
    }

    protected void setPropertiesMenuItemEnabled(ProfilePanel panel, ArcMoverFile selectedFile, boolean single) {
        propertiesMenuItem.setEnabled(
                panel != null && !panel.isLocked() && panel.supportsPropertiesAction(selectedFile, single));
    }

    /**
     * Whether there is at least one file selected
     * 
     * @param profilePanel
     *            the panel to check
     * @return whether at least one file is selected
     */
    protected boolean doesFilePanelHaveSelection(ProfilePanel profilePanel) {
        return (profilePanel != null && profilePanel.getSelectedRowCount() > 0);
    }

    /**
     * Whether there is exactly one file selected
     * 
     * @param profilePanel
     *            the panel to check
     * @return whether exactly one file is selected
     */
    private boolean doesFilePanelHaveSingleSelection(ProfilePanel profilePanel) {
        return (profilePanel != null && profilePanel.getSelectedRowCount() == 1);
    }

    public static void showMigratorInstance() {
        getInstance().showMigrator();
    }

    /**
     * Shows the migrator instance. This method makes a best attempt at bringing the migrator to the
     * front, working around some issues with Swing.
     */
    public void showMigrator() {
        showMigrator(true);
    }

    /**
     * Factored out the invocation of the self-destructive dialog because it was stealing focus
     * during window closing. Normally it should be safe though.
     * 
     * @param invoke
     *            Whether to use the self-destructive dialog or not
     */
    private void showMigrator(boolean invoke) {
        updateLockable();
        setVisible(true);
        // Un-iconify
        if (getState() == Frame.ICONIFIED) {
            setState(Frame.NORMAL);
        }
        toFront();
        requestFocus();
        final HCPDataMigrator migrator = this;
        if (invoke) {
            SelfDestructiveDialog.invoke(migrator);
        }
    }

    /**
     * Refreshes each panel, but only if it's unlocked
     */
    public void refreshPanels() {
        if (!leftProfilePanel.isLocked()) {
            leftProfilePanel.refreshPathAndFileList();
        }
        if (!rightProfilePanel.isLocked()) {
            rightProfilePanel.refreshPathAndFileList();
        }
    }

    /**
     * Refreshes each panel where it's using the same profile and current directory on that panel
     * matches the given directory, where version lists count as a directory match
     * 
     * @param profile
     *            The profile to look for
     * @param directory
     *            The directory to look for
     */
    public void refreshMatchingPanels(AbstractProfileBase profile, ArcMoverDirectory directory) {
        final String dirPath;
        if (directory.isVersionList()) {
            dirPath = directory.getParent().getPath();
        } else {
            dirPath = directory.getPath();
        }

        if (!leftProfilePanel.isLocked() && profile.equals(leftProfilePanel.getSelectedProfile())) {
            final String leftDirPath;
            final ArcMoverDirectory leftDir = leftProfilePanel.getCurrentDirectory();
            if (leftDir.isVersionList()) {
                leftDirPath = leftDir.getParent().getPath();
            } else {
                leftDirPath = leftDir.getPath();
            }

            if (dirPath.equals(leftDirPath)) {
                leftProfilePanel.refreshPathAndFileList();
            }
        }

        if (!rightProfilePanel.isLocked() && profile.equals(rightProfilePanel.getSelectedProfile())) {
            final String rightDirPath;
            final ArcMoverDirectory rightDir = rightProfilePanel.getCurrentDirectory();
            if (rightDir.isVersionList()) {
                rightDirPath = rightDir.getParent().getPath();
            } else {
                rightDirPath = rightDir.getPath();
            }

            if (dirPath.equals(rightDirPath)) {
                rightProfilePanel.refreshPathAndFileList();
            }
        }
    }
}