Java tutorial
// 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.panels; import com.archivas.clienttools.arcmover.gui.*; import com.archivas.clienttools.arcmover.gui.profileadapter.Hcp3AuthNamepaceProfilePanelAdapter; import com.archivas.clienttools.arcmover.gui.profileadapter.ProfilePanelAdapter; import com.archivas.clienttools.arcmover.gui.ssl.SSLCertificateDialog; import com.archivas.clienttools.arcmover.gui.util.FileListTable; import com.archivas.clienttools.arcmover.gui.util.FileListTableModel; import com.archivas.clienttools.arcmover.gui.util.GUIHelper; import com.archivas.clienttools.arcutils.api.ArcMoverEngine; import com.archivas.clienttools.arcutils.api.ArcMoverFactory; import com.archivas.clienttools.arcutils.api.jobs.DeleteJob; import com.archivas.clienttools.arcutils.api.jobs.SetMetadataJob; import com.archivas.clienttools.arcutils.config.HCPMoverProperties; import com.archivas.clienttools.arcutils.impl.adapter.StorageAdapterException; import com.archivas.clienttools.arcutils.impl.adapter.StorageAdapterLiteralException; import com.archivas.clienttools.arcutils.profile.*; import com.archivas.clienttools.arcutils.model.*; import com.archivas.clienttools.arcutils.utils.FileUtil; import com.archivas.clienttools.arcutils.utils.database.DBUtils; import org.apache.http.HttpStatus; import javax.net.ssl.SSLPeerUnverifiedException; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.plaf.basic.BasicComboBoxRenderer; import javax.swing.table.JTableHeader; import java.awt.*; import java.awt.event.*; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; public class ProfilePanel extends JPanel { // logging private static final Logger LOG = Logger.getLogger(ProfilePanel.class.getName()); public static final String NEWLINE = System.getProperty("line.separator"); public static final String VERSION_PREFIX = "Versions for: "; public static final int UI_FIRST_BATCH_SIZE = HCPMoverProperties.UI_FIRST_BATCH_SIZE.getAsInt(); private static final String BROWSE_LFS_TEXT = "Browse Local File System"; private FileListTableModel fileListModel; private FileListTable fileList; private JScrollPane fileListScrollPane; private JButton refreshButton; private JComboBox pathCombo; private PathComboActionListener pathComboActionListener; private ProfileComboBoxModel profileModel; private JComboBox profileSelectionCombo; private JButton upDirectoryButton; private JButton browseDirectoryButton; private JButton sslButton; private JButton homeDirectoryButton; private ArcMoverEngine arcMoverEngine = ArcMoverFactory.getInstance(); private ArcMoverDirectory currentDir; private ArcMoverFile selectedFile = null; private boolean selectionIsReadOnly = false; private boolean forceRefresh = false; private JPopupMenu rightClickMenu = new JPopupMenu(); private JMenuItem openMenuItem; private JMenuItem deleteMenuItem; private JMenuItem setMetadataMenuItem; private JMenuItem renameMenuItem; private JMenuItem refreshMenuItem; private JMenuItem mkdirMenuItem; private JMenuItem browseMenuItem; private JMenuItem propertiesMenuItem; /** * Enables/disables all subcomponents of this panel. This also toggles the ability to drag and * drop and conditionally freezes the ability to resize the file list column headers. * * @param enabled * Whether or not this panel is enabled */ private void subSetEnabled(final boolean enabled) { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "subSetEnabled is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } enableDragAndDrop(enabled); GUIHelper.enableComponent(this, enabled); setEnabled(true); // Re-enable the panel since GUIHelper.enableComponent disables it sslButton.setEnabled(enabled && profileModel.getProfileAdapter() != null && profileModel.getProfileAdapter().getSSLCertChain() != null); browseDirectoryButton.setEnabled(enabled && getSelectedProfile().getType() == ProfileType.FILESYSTEM); JTableHeader header = fileList.getTableHeader(); if (header != null) { header.setResizingAllowed(enabled); } } private int lockCount = 0; public static String LOCK_PROPERTY = "locked"; /** * Whether or not this profile panel is considered "locked" and is thus in a volatile state. No * actions relating to this panel should be attempted so long as it is locked. The ProfilePanel * will fire a LOCK_PROPERTY event when the lock status changes. * * @return Whether the panel is locked or not */ public boolean isLocked() { return lockCount > 0; } private void lock() { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "lock is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } lockCount++; updateLockState(); if (lockCount == 1) { firePropertyChange(LOCK_PROPERTY, false, true); } } private void unlock() { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "unlock is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } lockCount--; if (lockCount < 0) { String errMsg = "lockCount is less than zero: " + lockCount; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } updateLockState(); if (lockCount == 0) { firePropertyChange(LOCK_PROPERTY, true, false); } } private void updateLockState() { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "updateLockState is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } boolean locked = isLocked(); if (locked) { setCursor(HCPDataMigrator.waitCursor); } subSetEnabled(!locked); if (!locked) { setCursor(null); } } public ProfilePanel() { initGuiComponents(); initializeMouseListener(); initializeActionListeners(); initializeAddressBar(); enableDragAndDrop(true); initializeRightClickMenu(); } private void initGuiComponents() { fileListModel = new FileListTableModel(); fileList = new FileListTable(fileListModel); fileList.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { openSelectedFile(); e.consume(); } } }); fileList.setAutoCreateColumnsFromModel(true); fileList.setDropMode(DropMode.ON_OR_INSERT_ROWS); fileList.setFillsViewportHeight(true); fileList.setGridColor(new Color(-1)); fileListScrollPane = new JScrollPane(fileList); fileListScrollPane.setAutoscrolls(false); fileListScrollPane.setBackground(UIManager.getColor("TableHeader.background")); fileListScrollPane.setPreferredSize(new Dimension(100, 128)); fileListScrollPane.setEnabled(false); // // toolbar // final JToolBar toolBar1 = new JToolBar(); toolBar1.setBorderPainted(false); toolBar1.setFloatable(false); toolBar1.setRollover(true); toolBar1.putClientProperty("JToolBar.isRollover", Boolean.TRUE); homeDirectoryButton = new JButton(); homeDirectoryButton.setHorizontalAlignment(2); homeDirectoryButton.setIcon(GUIHelper.HOME_ICON); homeDirectoryButton.setMargin(new Insets(3, 3, 3, 3)); homeDirectoryButton.setText(""); homeDirectoryButton.setToolTipText("Go to home directory"); homeDirectoryButton.setEnabled(false); toolBar1.add(homeDirectoryButton); refreshButton = new JButton(); refreshButton.setHorizontalAlignment(2); refreshButton.setIcon(new ImageIcon(getClass().getResource("/images/refresh.gif"))); refreshButton.setMargin(new Insets(3, 3, 3, 3)); refreshButton.setText(""); refreshButton.setToolTipText("Refresh current directory listing"); refreshButton.setEnabled(false); toolBar1.add(refreshButton); upDirectoryButton = new JButton(); upDirectoryButton.setHideActionText(false); upDirectoryButton.setHorizontalAlignment(2); upDirectoryButton.setIcon(GUIHelper.UP_DIR_ICON); upDirectoryButton.setMargin(new Insets(3, 3, 3, 3)); upDirectoryButton.setToolTipText("Up"); upDirectoryButton.setEnabled(false); toolBar1.add(upDirectoryButton); browseDirectoryButton = new JButton(); browseDirectoryButton.setHideActionText(false); browseDirectoryButton.setHorizontalAlignment(2); browseDirectoryButton.setIcon(GUIHelper.DIRECTORY_ICON); browseDirectoryButton.setMargin(new Insets(3, 3, 3, 3)); browseDirectoryButton.setToolTipText(BROWSE_LFS_TEXT); browseDirectoryButton.setEnabled(false); toolBar1.add(browseDirectoryButton); profileModel = new ProfileComboBoxModel(); profileSelectionCombo = new JComboBox(profileModel); profileSelectionCombo.setEnabled(false); profileSelectionCombo.setToolTipText("Select a namespace profile"); profileSelectionCombo.setPrototypeDisplayValue("#"); pathCombo = new JComboBox(); pathCombo.setEditable(false); pathCombo.setEnabled(false); pathCombo.setToolTipText("Current directory path"); pathCombo.setPrototypeDisplayValue("#"); sslButton = new JButton(); sslButton.setAlignmentY(0.0f); sslButton.setBorderPainted(false); sslButton.setHorizontalAlignment(2); sslButton.setHorizontalTextPosition(11); sslButton.setIcon(new ImageIcon(getClass().getResource("/images/lockedstate.gif"))); sslButton.setMargin(new Insets(0, 0, 0, 0)); sslButton.setMaximumSize(new Dimension(20, 20)); sslButton.setMinimumSize(new Dimension(20, 20)); sslButton.setPreferredSize(new Dimension(20, 20)); sslButton.setText(""); sslButton.setToolTipText("View certificate"); sslButton.setEnabled(false); // // profile and toolbar buttons // JPanel profileAndToolbarPanel = new FixedHeightPanel(); profileAndToolbarPanel.setLayout(new BoxLayout(profileAndToolbarPanel, BoxLayout.X_AXIS)); profileAndToolbarPanel.add(profileSelectionCombo); profileAndToolbarPanel.add(Box.createHorizontalStrut(25)); profileAndToolbarPanel.add(toolBar1); // // Path & SSLCert button // JPanel pathPanel = new FixedHeightPanel(); pathPanel.setLayout(new BoxLayout(pathPanel, BoxLayout.X_AXIS)); pathCombo.setAlignmentY(CENTER_ALIGNMENT); pathPanel.add(pathCombo); pathPanel.add(Box.createHorizontalStrut(5)); sslButton.setAlignmentY(CENTER_ALIGNMENT); pathPanel.add(sslButton); // // Put it all together // setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); add(profileAndToolbarPanel); add(Box.createVerticalStrut(5)); add(pathPanel); add(Box.createVerticalStrut(5)); add(fileListScrollPane); setBorder(new EmptyBorder(12, 12, 12, 12)); } public void setFileFont(Font newFont) { pathCombo.setFont(newFont); fileList.setFileFont(newFont); repaint(); } public Font getFileFont() { return fileList.getFont(); } public void enableDragAndDrop(boolean flag) { flag = flag && !isSelectionReadOnly(); // Can't drag-and-drop into a read-only MoverTransferHandler moveTransferHandler = new MoverTransferHandler(this); fileList.setTransferHandler(moveTransferHandler); fileList.setDragEnabled(true); fileList.setDropMode(DropMode.INSERT_ROWS); moveTransferHandler.setDropEnabled(flag); } private void initializeMouseListener() { fileList.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { onFileListSelectionChangedAction(e); } }); fileList.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { onFileListMouseClicked(e); } public void mouseReleased(MouseEvent e) { onFileListMouseClicked(e); } public void mouseClicked(MouseEvent e) { // check if the user has double clicked on an item if (e.getClickCount() == 2) { onFileListMouseDoubleClicked(); } } }); } private void updateSelectedFile() { int selection = fileList.getSelectedRow(); if (selection >= 0) { selectedFile = fileListModel.getFileAt(selection); } else { selectedFile = null; } } // // process new user selection in the local dir list // private void onFileListSelectionChangedAction(ListSelectionEvent evt) { try { updateSelectedFile(); // Reset selections based on this list change if (selectedFile != null) { HCPDataMigrator.getInstance().selectionFromPanel(this); } HCPDataMigrator.getInstance().updateLockable(); updateRightClickMenu(); } catch (Exception e) { String msg = DBUtils.getErrorMessage("An error occurred selecting file", e); LOG.log(Level.WARNING, msg, e); GUIHelper.showMessageDialog(this, msg, "Error Selecting File", JOptionPane.ERROR_MESSAGE); } } /** * Called one time at window creation time */ private void initializeRightClickMenu() { openMenuItem = new JMenuItem("Open"); openMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { setCursor(HCPDataMigrator.waitCursor); openSelectedRow(); } finally { setCursor(null); } } }); rightClickMenu.add(openMenuItem); rightClickMenu.addSeparator(); deleteMenuItem = new JMenuItem("Delete"); deleteMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { setCursor(HCPDataMigrator.waitCursor); deleteSelectedRows(); } finally { setCursor(null); } } }); rightClickMenu.add(deleteMenuItem); setMetadataMenuItem = new JMenuItem("Set Metadata"); setMetadataMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { setCursor(HCPDataMigrator.waitCursor); setMetadataOnSelectedRows(); } finally { setCursor(null); } } }); rightClickMenu.add(setMetadataMenuItem); renameMenuItem = new JMenuItem("Rename"); renameMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { setCursor(HCPDataMigrator.waitCursor); renameSelectedRow(); } finally { setCursor(null); } } }); rightClickMenu.add(renameMenuItem); rightClickMenu.addSeparator(); refreshMenuItem = new JMenuItem("Refresh"); refreshMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { refreshPathAndFileList(); } }); rightClickMenu.add(refreshMenuItem); mkdirMenuItem = new JMenuItem("New Directory"); mkdirMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { mkdirAction(); } }); rightClickMenu.add(mkdirMenuItem); browseMenuItem = new JMenuItem(BROWSE_LFS_TEXT); browseMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setPathToBrowseDirectory(); } }); rightClickMenu.add(browseMenuItem); rightClickMenu.addSeparator(); propertiesMenuItem = new JMenuItem("Properties"); propertiesMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { setCursor(HCPDataMigrator.waitCursor); displaySelectedObjectProperties(); } finally { setCursor(null); } } }); rightClickMenu.add(propertiesMenuItem); } public boolean supportsOpenAction(ArcMoverFile file, boolean single) { ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); return supportsOpenAction(file, single, profileAdapter); } public boolean supportsDeleteAction(ArcMoverFile file, boolean single) { ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); return supportsDeleteAction(file, single, profileAdapter); } public boolean supportsSetMetadataAction(ArcMoverFile file, boolean single) { ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); return supportsSetMetadataAction(file, single, profileAdapter); } public boolean supportsRenameAction(ArcMoverFile file, boolean single) { ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); return supportsRenameAction(file, single, profileAdapter); } public boolean supportsRefreshAction(ArcMoverFile file, boolean single) { ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); return supportsRefreshAction(file, single, profileAdapter); } public boolean supportsMkdirAction(ArcMoverFile file, boolean single) { ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); return supportsMkdirAction(file, single, profileAdapter); } public boolean supportsPropertiesAction(ArcMoverFile file, boolean single) { ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); return supportsPropertiesAction(file, single, profileAdapter); } private boolean supportsOpenAction(ArcMoverFile file, boolean single, ProfilePanelAdapter profileAdapter) { return profileAdapter != null && profileAdapter.supportsOpenAction(file, single); } private boolean supportsDeleteAction(ArcMoverFile file, boolean single, ProfilePanelAdapter profileAdapter) { return profileAdapter != null && profileAdapter.supportsDeleteAction(file, single); } private boolean supportsSetMetadataAction(ArcMoverFile file, boolean single, ProfilePanelAdapter profileAdapter) { return profileAdapter != null && profileAdapter.supportsSetMetadataAction(file, single); } private boolean supportsRenameAction(ArcMoverFile file, boolean single, ProfilePanelAdapter profileAdapter) { return profileAdapter != null && profileAdapter.supportsRenameAction(file, single); } private boolean supportsRefreshAction(ArcMoverFile file, boolean single, ProfilePanelAdapter profileAdapter) { return profileAdapter != null && profileAdapter.supportsRefreshAction(file, single); } private boolean supportsMkdirAction(ArcMoverFile file, boolean single, ProfilePanelAdapter profileAdapter) { return profileAdapter != null && profileAdapter.supportsMkdirAction(file, single); } private boolean supportsPropertiesAction(ArcMoverFile file, boolean single, ProfilePanelAdapter profileAdapter) { return profileAdapter != null && profileAdapter.supportsPropertiesAction(file, single); } private void updateRightClickMenu() { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "updateRightClickMenu is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); if (profileAdapter != null) { boolean single = (getSelectedRowCount() == 1); openMenuItem.setEnabled(supportsOpenAction(selectedFile, single, profileAdapter)); boolean showDelete = !currentDir.isVersionList() && supportsDeleteAction(selectedFile, single, profileAdapter) && !JobDialog.isOpen(); // Can't start a delete job when a job already exists deleteMenuItem.setEnabled(showDelete); boolean showSetMetadata = !currentDir.isVersionList() && supportsSetMetadataAction(selectedFile, single, profileAdapter) && !JobDialog.isOpen(); // Can't start a set metadata job when a job already // exists setMetadataMenuItem.setEnabled(showSetMetadata); boolean showRename = supportsRenameAction(selectedFile, single, profileAdapter) && !JobDialog.isOpen(); renameMenuItem.setEnabled(showRename); refreshMenuItem.setEnabled(supportsRefreshAction(selectedFile, single, profileAdapter)); boolean showMkdir = !currentDir.isVersionList() && supportsMkdirAction(selectedFile, single, profileAdapter); mkdirMenuItem.setEnabled(showMkdir); propertiesMenuItem.setEnabled(supportsPropertiesAction(selectedFile, single, profileAdapter)); } browseMenuItem.setEnabled(FileSystemProfile.LOCAL_FILESYSTEM_PROFILE == getSelectedProfile()); } private void initializeActionListeners() { // // initialize local navigation buttons // upDirectoryButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setPathAndFileListUpDirectory(); } }); homeDirectoryButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setPathAndFileListToHomeDirectory(); } }); refreshButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { refreshPathAndFileList(); } }); browseDirectoryButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setPathToBrowseDirectory(); } }); profileSelectionCombo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { onProfileSelected(); } }); sslButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { setCursor(HCPDataMigrator.waitCursor); showSSLCert(); } finally { setCursor(null); } } }); } private String browseDirectory() { if (getSelectedProfile() != FileSystemProfile.LOCAL_FILESYSTEM_PROFILE) { // browse only supported on LFS return null; } String loadPath = currentDir.getPath(); JFileChooser chooser = new JFileChooser(loadPath); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int retVal = chooser.showOpenDialog(this); if (retVal == JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile().getAbsolutePath(); } else { return null; } } private void setPathToBrowseDirectory() { try { String path = browseDirectory(); if (path != null) { setPathAndFileListToDirectory(path); } } catch (Exception e) { String msg = DBUtils.getErrorMessage("An error occurred selecting a directory", e); LOG.warning(msg); GUIHelper.showMessageDialog(this, msg, "Error Selecting Directory", JOptionPane.ERROR_MESSAGE); } } /** * The basic algorithm here is: Lock the panel In an asynchronous worker thread: Try to get * directory for last history item If error try to get directory for home If error back out * reset to original settings If success update current state Unlock the panel on completion of * the worker thread */ private void onProfileSelected() { lock(); new SetProfileWorker().execute(); } class SetProfileWorker extends SwingWorker { @Override protected Object doInBackground() throws Exception { try { setProfile(); } catch (Exception e) { LOG.log(Level.WARNING, "Error during setProfile", e); } return null; } @Override protected void done() { unlock(); } } private void setProfile() { AbstractProfileBase profile = profileModel.getSelectedProfile(); ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); String[] paths = HistoryManager.openHistory(profile); try { if (!ArcMoverFactory.getInstance().testConnection(profile)) { throw new IllegalStateException(String.format("Cannot connect to profile %s.", profile.getName())); } // If we have no history then pretend we only had the home directory if (paths.length == 0) { paths = new String[] { profileAdapter.getHomeDirectory().getPath() }; } String path = paths[0]; boolean tryHome = false; ArcMoverDirectory dir = null; try { List<String> fileListColumns = profileAdapter.getFileListColumns(); path = stripVersioningIfUnsupported(path); boolean isVersioned = isVersioned(path); if (isVersioned) { dir = profileAdapter.getVersions(filePath(path)); fileListColumns = ((Hcp3AuthNamepaceProfilePanelAdapter) profileAdapter) .getVersionListColumns(); } else { dir = profileAdapter.getDirectory(path); } setFileListToDirectory(dir, fileListColumns); } catch (Exception e) { LOG.log(Level.WARNING, "An error occurred opening directory " + path + " on profile " + profile.getName(), e); Throwable rootCause = e; while (rootCause.getCause() != null) { rootCause = rootCause.getCause(); } if (rootCause instanceof SSLPeerUnverifiedException) { throw e; // Don't bother with the home directory, user canceled the SSL // verification } tryHome = true; } if (tryHome) { // Try home directory instead String badPath = path; dir = profileAdapter.getHomeDirectory(); path = dir.getPath(); // We know home is not versioned so we can use the standard file list columns setFileListToDirectory(dir, profileAdapter.getFileListColumns()); // Home succeeded, so remove the bad path HistoryManager.removeFromHistory(profile, badPath); paths = HistoryManager.addToHistory(profile, path); } // success: setCurrent(dir, path, paths, profile); profileSelectionCombo.setToolTipText(profile.getName()); HCPDataMigrator.getInstance().saveSelectedProfile(this, profile.getName()); } catch (Exception e) { String msg = "An error occurred opening profile " + profile.getName(); Throwable rootCause = e; while (rootCause.getCause() != null) { rootCause = rootCause.getCause(); } if (rootCause instanceof StorageAdapterLiteralException) { Integer statusCode = ((StorageAdapterLiteralException) rootCause).getStatusCode(); if (statusCode != null && statusCode == HttpStatus.SC_FORBIDDEN) { // 403 switch (profile.getType()) { case HCP60: case HCP50: case HCP: msg = msg + ": " + "Access denied, please check that the proper protocols are enabled on" + " the namespace and that the user has the correct permissions"; break; case HCAP2: case HCP_DEFAULT: msg = msg + ": " + "Access denied, please check that the proper protocols are enabled"; break; default: msg = msg + ": " + rootCause.getMessage(); break; } } else { msg = msg + ": " + rootCause.getMessage(); } } else if (rootCause instanceof SSLPeerUnverifiedException) { msg = String.format("SSL certificate is not trusted for profile %s.", profile.getName()); } AbstractProfileBase currentProfile = pathComboActionListener.getCurrentProfile(); // If it's null or isn't a change, set it to the local filesystem profile if (currentProfile == null || currentProfile == profile) { currentProfile = FileSystemProfile.LOCAL_FILESYSTEM_PROFILE; } final AbstractProfileBase currentProfileR = currentProfile; GUIHelper.invokeAndWait(new Runnable() { public void run() { profileSelectionCombo.setSelectedItem(currentProfileR); } }, "reset the current profile after an error"); GUIHelper.showMessageDialog(this, msg, "Error Opening Profile", JOptionPane.ERROR_MESSAGE); LOG.log(Level.SEVERE, msg, e); } } /** * This updates the current state, updating tooltips, calling to update the file menu and right * click menus. Works on the EDT synchronously. * * @param dir * The new current directory * @param path * The new current path * @param profile * The new current profile */ private void setCurrent(final ArcMoverDirectory dir, final String path, final String[] paths, final AbstractProfileBase profile) { if (SwingUtilities.isEventDispatchThread()) { String errMsg = "setCurrent is on the EDT but it should not be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } GUIHelper.invokeAndWait(new Runnable() { public void run() { setCurrentAux(dir, path, paths, profile); } }, "set current state"); } private void setCurrentAux(ArcMoverDirectory dir, String path, String[] paths, AbstractProfileBase profile) { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "setCurrentAux is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } // if this is LFS, we want a browse option at the top if (getSelectedProfile() == FileSystemProfile.LOCAL_FILESYSTEM_PROFILE) { String[] pathsWithBrowse = new String[paths.length + 1]; pathsWithBrowse[0] = BROWSE_LFS_TEXT; System.arraycopy(paths, 0, pathsWithBrowse, 1, paths.length); paths = pathsWithBrowse; } pathCombo.setModel(new DefaultComboBoxModel(paths)); pathCombo.setEditable(true); currentDir = dir; pathComboActionListener.setCurrentPath(path); pathComboActionListener.setCurrentProfile(profile); profile.setDisplayPath(path); forceRefresh = false; pathCombo.setSelectedItem(path); pathCombo.setToolTipText(profile.decode(path)); HCPDataMigrator.getInstance().updateLockable(); updateRightClickMenu(); } private void setPathAndFileListUpDirectory() { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "setPathAndFileListUpDirectory is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } try { ArcMoverDirectory parent = currentDir.getParent(); if (parent != null) { setPathAndFileListToDirectory(parent.getPath()); } } catch (Exception e1) { JOptionPane.showMessageDialog(this, "An error occurred opening directory. See logs for more information.", "Error Opening Directory", JOptionPane.ERROR_MESSAGE); LOG.log(Level.WARNING, "An error occurred getting a directory listing: " + e1.toString(), e1); } } private void setPathAndFileListToHomeDirectory() { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "setPathAndFileListToHomeDirectory is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } try { ProfilePanelAdapter profileAdapter = profileModel.getProfileAdapter(); setPathAndFileListToDirectory(profileAdapter.getHomeDirectory().getPath()); } catch (Exception e) { String errMsg = DBUtils.getErrorMessage("An error occurred opening home directory", e); GUIHelper.showMessageDialog(this, errMsg, "Error Opening Directory", JOptionPane.ERROR_MESSAGE); LOG.log(Level.WARNING, "An error occurred getting the home directory listing: " + e.toString(), e); } } public void refreshPathAndFileList() { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "refreshPathAndFileList is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } try { forceRefresh = true; if (currentDir.isVersionList()) { setPathAndFileListToDirectory(versionPath(currentDir)); } else { setPathAndFileListToDirectory(currentDir.getPath()); } } catch (Exception e1) { JOptionPane.showMessageDialog(this, "An error occurred refreshing the directory listing. See logs for more information.", "Error Reloading Directory", JOptionPane.ERROR_MESSAGE); LOG.log(Level.WARNING, "An error occurred refreshing a directory listing: " + e1.toString(), e1); } } /** * Updates the path combo box and the file list to reflect the specified directory * * @param path * - * @throws Exception */ private void setPathAndFileListToDirectory(String path) { // pathCombo.setSelectedItem() causes the action listener for the pathCombo to be fired, // which will update the file list if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "setPathAndFileListToDirectory is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } pathCombo.setSelectedItem(path); } /** * This sets the file list for this panel to the files in the given directory. Can throw a * StorageAdapterException when getting the file list for the directory. The actual work on the * fileList is completed on the EDT for thread safety. * * @param dir * The directory to display * @param colsToDisplay * Columns to display in the file list * @throws Exception * An exception thrown by dir.getFileList or SwingUtilities.invokeAndWait */ private void setFileListToDirectory(ArcMoverDirectory dir, List<String> colsToDisplay) throws Exception { // Should not be run on the EDT if (SwingUtilities.isEventDispatchThread()) { String errMsg = "setFileListToDirectory is on the EDT but it should not be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } List<ArcMoverFile> files = dir.getFileList(UI_FIRST_BATCH_SIZE); int badElementCnt = dir.getFileListBadElementCnt(); List<ArcMoverFile> sortedDirs = new ArrayList<ArcMoverFile>(); List<ArcMoverFile> sortedFiles = new ArrayList<ArcMoverFile>(); List<ArcMoverFile> sortedSymlinks = new ArrayList<ArcMoverFile>(); ArcMoverFile mostRecentVersion = null; boolean isVersionList = dir.isVersionList(); for (ArcMoverFile f : files) { // if f is a directory or is a link to a directory, show as directory if (f.isDirectory()) { sortedDirs.add(f); } else if (f.isSymlink()) { sortedSymlinks.add(f); } else { if (mostRecentVersion == null) { if (f.getMetadata().hasVersionNumber()) { mostRecentVersion = f; } } else { if (isVersionList && f.getMetadata().hasVersionNumber()) { mostRecentVersion.setLatestVersion(false); if (f.getMetadata().getVersionNumber() > mostRecentVersion.getMetadata() .getVersionNumber()) { mostRecentVersion = f; } } } sortedFiles.add(f); } } if (mostRecentVersion != null) { mostRecentVersion.setLatestVersion(true); } Collections.sort(sortedDirs); Collections.sort(sortedSymlinks); if (isVersionList) { Collections.sort(sortedFiles, ArcMoverVersionedFileComparator.getInstance()); } else { Collections.sort(sortedFiles, ArcMoverFileDefaultComparator.getInstance()); } files.clear(); files.addAll(sortedDirs); files.addAll(sortedSymlinks); files.addAll(sortedFiles); GUIHelper.invokeAndWait( getFileListRunner(colsToDisplay, files, isVersionList, badElementCnt, dir.getPath()), "get file list"); } private void setFileList(final List<String> colsToDisplay, final List<ArcMoverFile> files, final boolean isVersionList, int badElementCnt, String dirPath) { fileListModel = new FileListTableModel(new ArrayList<String>(colsToDisplay)); fileList.setModel(fileListModel); fileList.setColumnCellRenderers(); fileList.setListData(new ArrayList<ArcMoverFile>(files)); if (isVersionList) { fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } else { fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); } if (badElementCnt > 0) { String errMsg = String.format("Warning: omitted %d unsupported filename(s) in directory listing for %s", badElementCnt, profileModel.getSelectedProfile().decode(dirPath)); LOG.log(Level.WARNING, errMsg); if (HCPMoverProperties.DISPLAY_WARNINGS_FOR_BAD_ELEMENTS_IN_DIRECTORY_LISTINGS.getAsBoolean()) { GUIHelper.showMessageDialog(this, errMsg, "", JOptionPane.WARNING_MESSAGE); } } } private Runnable getFileListRunner(final List<String> colsToDisplay, final List<ArcMoverFile> files, final boolean isVersionList, final int badElementCnt, final String dirPath) { return new Runnable() { public void run() { setFileList(colsToDisplay, files, isVersionList, badElementCnt, dirPath); } }; } protected void initializeAddressBar() { pathComboActionListener = new PathComboActionListener(); pathCombo.addActionListener(pathComboActionListener); pathCombo.setRenderer(new PathComboBoxRenderer()); pathCombo.setEditor(new PathComboBoxEditor()); /* * // call onCancel() on ESCAPE pathCombo.registerKeyboardAction(new ActionListener() { * public void actionPerformed(ActionEvent e) { if (fileList.isEnabled()) { try { * pathCombo.setModel(new * DefaultComboBoxModel(HistoryManager.openHistory(getMoverProfile()))); * pathCombo.setPopupVisible(false); } catch (Exception e1) { String msg = * "Unexpected Error"; LOG.log(Level.WARNING, msg, e1); * GUIHelper.showMessageDialog(ProfilePanel.this, msg, "Error", JOptionPane.ERROR_MESSAGE); * } } } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), * JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); * * pathCombo.setModel(new * DefaultComboBoxModel(HistoryManager.openHistory(getMoverProfile()))); * * pathCombo.setPrototypeDisplayValue("#"); */ } public void refreshProfiles() { profileModel.loadProfiles(); } public void setSelectedProfile(AbstractProfileBase profile) { profileSelectionCombo.setSelectedItem(profile); } public AbstractProfileBase getSelectedProfile() { return profileModel.getSelectedProfile(); } public ArcMoverDirectory getCurrentDirectory() { return currentDir; } // for drag & drop support, we need to know what file is at a particular row public ArcMoverFile getFileAt(int row) { ArcMoverFile moverFile = null; try { moverFile = fileListModel.getFileAt(row); } catch (IndexOutOfBoundsException e) { return null; } return moverFile; } public void resizeFileList() { GUIHelper.autoResizeColWidth(fileList); } public void clearSelection() { fileList.clearSelection(); } /** * Wrapper to return the selected files from the file list model. */ public ArcMoverFile[] getSelectedFiles() { int[] rows = fileList.getSelectedRows(); ArcMoverFile[] files = new ArcMoverFile[rows.length]; for (int i = 0; i < rows.length; i++) { files[i] = fileListModel.getFileAt(rows[i]); } return files; } /** * Gets the number of selected, possibly-deleted files * * @return the number of selected, possibly-deleted files */ public int getSelectedRowCount() { return fileList.getSelectedRowCount(); } /** * Gets the number of selected, non-deleted files * * @return the number of selected, non-deleted files */ public int getNonDeletedSelectedFileCount() { int count = 0; for (int i : fileList.getSelectedRows()) { ArcMoverFile file = fileListModel.getFileAt(i); if (!file.isDeleted()) { count++; } } return count; } private void onFileListMouseClicked(MouseEvent evt) { try { if (evt.isPopupTrigger()) { updateSelectedFile(); updateRightClickMenu(); rightClickMenu.show(fileList, evt.getX(), evt.getY()); } } catch (Exception e1) { String msg = "Unexpected error showing right-click menu"; LOG.log(Level.WARNING, msg, e1); JOptionPane.showMessageDialog(this, msg, "Error", JOptionPane.ERROR_MESSAGE); } } protected void openSelectedFile() { if (!SwingUtilities.isEventDispatchThread()) { String errMsg = "openSelectedFile is not on the EDT but it should be"; IllegalStateException ex = new IllegalStateException(errMsg); LOG.log(Level.SEVERE, errMsg, ex); throw ex; } try { if (selectedFile != null) { // check if this item is a directory if (selectedFile.isDirectory()) { ArcMoverDirectory newDir = (ArcMoverDirectory) selectedFile; if (!newDir.canRead()) { GUIHelper.showMessageDialog(this, "An error occurred reading directory " + profileModel.getSelectedProfile().decode(newDir.getPath()), "Error Reading Directory", JOptionPane.ERROR_MESSAGE); } else { setPathAndFileListToDirectory(newDir.getPath()); } } else if (selectedFile.isSymlink()) { // do nothing } else if (!selectedFile.getParent().isVersionList() && profileModel.getSelectedProfile().supportsVersioning()) { // This is file with versions, and we're not in the version list. setPathAndFileListToDirectory(versionPath(selectedFile)); } else { openSelectedRow(); } } } catch (Exception e) { String msg = "Unexpected error"; LOG.log(Level.WARNING, msg, e); JOptionPane.showMessageDialog(this, msg, "Error", JOptionPane.ERROR_MESSAGE); } } protected void onFileListMouseDoubleClicked() { updateSelectedFile(); openSelectedFile(); // The right click menu can be different based on whether you went into a versions listing // or not updateRightClickMenu(); } private String filePath(String versionPath) { return versionPath.substring(VERSION_PREFIX.length()); } private String versionPath(ArcMoverFile file) { return VERSION_PREFIX + file.getPath(); } /** */ public final void openSelectedRow() { if (getSelectedRowCount() != 1) { JOptionPane.showMessageDialog(this, "You may only open one object at a time", "", JOptionPane.INFORMATION_MESSAGE); } if (!selectedFile.isDirectory()) { OpenFileThread openFileThread = new OpenFileThread(selectedFile, profileModel.getProfileAdapter()); openFileThread.execute(); } } public final void renameSelectedRow() { try { if (getSelectedRowCount() != 1) { JOptionPane.showMessageDialog(this, "You may only rename one object at a time", "", JOptionPane.INFORMATION_MESSAGE); return; } // show rename dialog String newFileName; String oldFileName = selectedFile.getFileName(); // The slugs are stand-ins for file type in messages final String lSlug = (selectedFile.isDirectory() ? "directory" : "file"); final String uSlug = (selectedFile.isDirectory() ? "Directory" : "File"); do { final String message = String.format("New %s name", lSlug); final String title = String.format("Rename %s", uSlug); // get new file name from user newFileName = (String) JOptionPane.showInputDialog(this.getParent(), message, title, JOptionPane.QUESTION_MESSAGE, null, null, selectedFile.getFileName()); // check if user pressed 'cancel' button if (newFileName == null) { LOG.info("Rename operation canceled"); return; } // validate the new file name // TODO: Complete validation based on target OS is not performed at this time. If // needed, that should be added here. if (newFileName.contains("/") || newFileName.contains("\\")) { final String errorMessage = String.format("Invalid %s name", lSlug); JOptionPane.showMessageDialog(this.getParent(), errorMessage, "Rename", JOptionPane.WARNING_MESSAGE); newFileName = null; } } while ((newFileName == null) || (newFileName.length() <= 0)); try { boolean success; success = arcMoverEngine.renameTo(profileModel.getSelectedProfile(), selectedFile.getParent().getPath(), oldFileName, newFileName); if (!success) { throw new IOException(String.format("Could not rename %s.", lSlug)); } LOG.info("Renamed " + oldFileName + " to " + newFileName); HCPDataMigrator.getInstance().refreshMatchingPanels(profileModel.getSelectedProfile(), currentDir); } catch (Exception e1) { String uiErrorMsg; String logErrorMsg; logErrorMsg = "Could not rename " + selectedFile.getPath() + " to " + newFileName; uiErrorMsg = DBUtils.getErrorMessage("Error renaming " + selectedFile.getPath(), e1); LOG.log(Level.WARNING, logErrorMsg, e1); GUIHelper.showMessageDialog(this.getParent(), uiErrorMsg, "Rename", JOptionPane.ERROR_MESSAGE); } } catch (RuntimeException e1) { // TODO: Implement Proper Error Handling LOG.log(Level.WARNING, "Unexpected Exception", e1); throw e1; } } public final void deleteSelectedRows() { try { ArcMoverFile[] selectedFiles = getSelectedFiles(); if (selectedFiles.length > 0) { List<ArcDeleteFile> fileNameList = new ArrayList<ArcDeleteFile>(); // add each selected file to the list for (ArcMoverFile nextFile : selectedFiles) { ArcDeleteFile nextDeleteFile = new ArcDeleteFile(nextFile); fileNameList.add(nextDeleteFile); } DeleteJob job = new DeleteJob(getSelectedProfile(), fileNameList); job.setSourcePath(getCurrentDirectory().getPath()); // won't return until the job window is closed JobDialog.openNewJob(job); refreshPathAndFileList(); } } catch (JobAlreadyOpenException jaoe) { JOptionPane.showMessageDialog(this, "Another job is already open.\nOnly one job may be open at a time.", "Error", JOptionPane.ERROR_MESSAGE); JobDialog.showJob(); } catch (Exception e) { String msg = DBUtils.getErrorMessage("An error occurred deleting selected items", e); LOG.log(Level.WARNING, msg, e); GUIHelper.showMessageDialog(this, msg, "Error", JOptionPane.ERROR_MESSAGE); } } public final void setMetadataOnSelectedRows() { try { ArcMoverFile[] selectedFiles = getSelectedFiles(); if (selectedFiles.length > 0) { List<ArcSetMetadataFile> fileNameList = new ArrayList<ArcSetMetadataFile>(); // add each selected file to the list for (ArcMoverFile nextFile : selectedFiles) { ArcSetMetadataFile nextMdFile = new ArcSetMetadataFile(nextFile); fileNameList.add(nextMdFile); } SetMetadataJob job = new SetMetadataJob(getSelectedProfile(), fileNameList); job.setSourcePath(getCurrentDirectory().getPath()); // won't return until the job window is closed JobDialog.openNewJob(job); refreshPathAndFileList(); } } catch (JobAlreadyOpenException jaoe) { JOptionPane.showMessageDialog(this, "Another job is already open.\nOnly one job may be open at a time.", "Error", JOptionPane.ERROR_MESSAGE); JobDialog.showJob(); } catch (Exception e) { String msg = DBUtils.getErrorMessage("An error occurred setting metadata on selected items", e); LOG.log(Level.WARNING, msg, e); GUIHelper.showMessageDialog(this, msg, "Error", JOptionPane.ERROR_MESSAGE); } } public void displaySelectedObjectProperties() { if (getSelectedRowCount() != 1) { JOptionPane.showMessageDialog(this, "You may only display properties for one object at a time", "", JOptionPane.INFORMATION_MESSAGE); } String path = null; try { path = selectedFile.getPath(); profileModel.getProfileAdapter().displayObjectProperties(selectedFile); } catch (Exception e) { String msg = DBUtils.getErrorMessage( "An error occurred displaying properties for " + profileModel.getSelectedProfile().decode(path), e); LOG.log(Level.WARNING, msg, e); GUIHelper.showMessageDialog(this, msg, "Error Displaying Properties", JOptionPane.ERROR_MESSAGE); } } public void mkdirAction() { lock(); new mkdirWorker().execute(); } class mkdirWorker extends SwingWorker { @Override protected Object doInBackground() throws Exception { mkdirActionAux(); return null; } @Override protected void done() { unlock(); } } public void mkdirActionAux() { try { String newDirName; String newDirPath = null; do { // get new directory name from user newDirName = (String) JOptionPane.showInputDialog(this.getParent(), "Name of new directory", "New Directory", JOptionPane.QUESTION_MESSAGE, null, null, "directory name"); // check if user pressed 'cancel' button if (newDirName == null) { LOG.info("Mkdir cancelled\n"); return; } /** * Validate the new directory name TODO: Complete validation based to target OS The * forward slash is never valid. On a local file system the file separator is * invalid. */ ProfileType type = getSelectedProfile().getType(); if (newDirName.contains("/") || (type == ProfileType.FILESYSTEM && newDirName.contains(File.separator)) || (type == ProfileType.HCAP2 && newDirName.contains("&"))) { JOptionPane.showMessageDialog(this.getParent(), "Invalid directory name", "Error", JOptionPane.WARNING_MESSAGE); newDirName = null; continue; } // build new directory path from current directory and user supplied name. We must // encode the part just entered by the user String encodedDirName = newDirName; try { encodedDirName = profileModel.getSelectedProfile().encode(newDirName); } catch (Exception e) { JOptionPane.showMessageDialog(this.getParent(), "An error occurred encoding the directory name", "Error", JOptionPane.WARNING_MESSAGE); LOG.log(Level.WARNING, String.format("An error occurred encoding directory <%s>", newDirName)); newDirName = null; continue; } newDirPath = FileUtil.resolvePath(currentDir.getPath(), encodedDirName, profileModel.getSelectedProfile().getPathSeparator()); } while (newDirName == null || newDirName.length() <= 0 || newDirPath == null); LOG.info("Creating new directory " + newDirPath); try { if (!arcMoverEngine.mkdir(profileModel.getSelectedProfile(), newDirPath)) { String msg = "Could not create directory: Permission Denied"; throw new IOException(msg); } final ArcMoverDirectory dir = profileModel.getProfileAdapter().getDirectory(newDirPath); GUIHelper.invokeAndWait(new Runnable() { public void run() { refreshPathAndFileList(); // refresh the other panel if necessary, but it won't do this panel because // we are currently locked HCPDataMigrator.getInstance().refreshMatchingPanels(profileModel.getSelectedProfile(), currentDir); } }, "refresh after creating new directory"); } catch (Exception e) { String uiErrorMsg; String logErrorMsg; if (e instanceof StorageAdapterException) { logErrorMsg = e.getMessage(); uiErrorMsg = e.getMessage(); } else { logErrorMsg = "Could not create directory: " + newDirPath; uiErrorMsg = DBUtils.getErrorMessage( "Error creating directory " + profileModel.getSelectedProfile().decode(newDirPath), e); } LOG.log(Level.WARNING, logErrorMsg, e); GUIHelper.showMessageDialog(this.getParent(), uiErrorMsg, "Create Directory Error", JOptionPane.ERROR_MESSAGE); } } catch (RuntimeException e1) { // TODO: Implement Proper Error Handling LOG.log(Level.WARNING, "Unexpected Exception", e1); throw e1; } } private void showSSLCert() { new SSLCertificateDialog((HCAPProfile) getSelectedProfile()).setVisible(true); } public ArcMoverFile getSelectedFile() { return selectedFile; } // Version listings are read only public boolean isSelectionReadOnly() { return selectionIsReadOnly || (currentDir != null && currentDir.isVersionList()); } /** * Listener for path changes Basic algorithm: If not forcing a refresh and the new path is the * same as the old path don't do anything Lock the panel In an asynchronous worker thread: Call * into setFileListToDirectory If error, show an error If successful Update history Update panel * Update right-click menu Unlock panel on completion of worker thread */ class PathComboActionListener implements ActionListener { // We need to declare the comboBoxChanged action command explicitly -- it is not provided // for us. private static final String CHANGE_COMMAND = "comboBoxChanged"; private String currentPath = null; private AbstractProfileBase currentProfile = null; public void actionPerformed(ActionEvent e) { // Only want to deal with "change" commands because of how we deal with state // transitions if (e.getActionCommand().equals(CHANGE_COMMAND)) { String newPath = null; if (getSelectedProfile() == FileSystemProfile.LOCAL_FILESYSTEM_PROFILE && pathCombo.getSelectedIndex() == 0 && BROWSE_LFS_TEXT.equals(pathCombo.getSelectedItem())) { // we're going to browse the local file system; the first item in the list in // this case was Browse newPath = browseDirectory(); forceRefresh = true; // "Browse ..." is currently displayed; need to refresh no // matter what if (newPath == null) { // the user cancelled the browse action, so we need to go back to the // old path that was displayed. We can't just return because "Browse ... " // is currently displayed as the path newPath = currentPath; } } else { newPath = (String) pathCombo.getSelectedItem(); } if (!forceRefresh && currentPath != null && currentPath.equals(newPath) && profileModel.getSelectedProfile() != null && profileModel.getSelectedProfile().equals(currentProfile)) { return; } lock(); new SetPathWorker(newPath, currentPath).execute(); } } public void setCurrentPath(String path) { this.currentPath = path; } public void setCurrentProfile(AbstractProfileBase profile) { this.currentProfile = profile; } public AbstractProfileBase getCurrentProfile() { return currentProfile; } } class PathComboBoxRenderer extends BasicComboBoxRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if (value != null && getSelectedProfile() != null) { value = getSelectedProfile().decode((String) value); } JComponent comp = (JComponent) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); comp.setToolTipText(String.valueOf(value)); return comp; } } /** * This editor displays decoded strings, so they are in user-readable form. * * However, the getItem() and setItem() methods return/take encoded strings. * * When the user types into the editor, the visible field is in decoded form, so it is encoded * when the user accepts the entry (hits enter.) The only issue is that since what is displayed * is decoded (and what the user is entering is decoded), if you try to edit (e.g. append to) a * decoded string, and the decoded string lost info (i.e. has a replacement char in it), it * doesn't work. So, the solution is to simply navigate with clicking into dirs with unsupported * chars, instead of trying to manually type at the end. */ class PathComboBoxEditor implements ComboBoxEditor { private JTextField editField = new JTextField("", 9); private String valueToReturn; // this may be different from the value displayed, as the // displayed value is decoded public PathComboBoxEditor() { editField.setBorder(null); editField.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // take the value just typed by the user, encode it, and set the selected item // to that. // If the version prefix is on the path, only encode the portion after the // version prefix. String path = editField.getText().trim(); if (isVersioned(path)) { valueToReturn = VERSION_PREFIX + getSelectedProfile().encode(path.substring(VERSION_PREFIX.length())); } else { valueToReturn = getSelectedProfile().encode(path); } pathCombo.setSelectedItem(valueToReturn); } }); } public Component getEditorComponent() { return editField; } public void setItem(Object anObject) { if (anObject != null) { valueToReturn = anObject.toString(); editField.setText(getSelectedProfile().decode(valueToReturn)); } } public Object getItem() { return valueToReturn; } public void selectAll() { editField.selectAll(); } public void addActionListener(ActionListener l) { editField.addActionListener(l); } public void removeActionListener(ActionListener l) { editField.removeActionListener(l); } } /** * Returns whether the path represents a versioned file * * @param path * The path * @return whether the path is versioned (begins with the version prefix) */ private boolean isVersioned(final String path) { return path.startsWith(VERSION_PREFIX); } /** * Removes the versioning parts of the path (resolving down to the containing directory) if * versioning is not supported by the selected profile * * @param path * The path * @return The original path if no change is required, the unversioned containing directory * otherwise * @throws StorageAdapterException * - */ private String stripVersioningIfUnsupported(String path) throws StorageAdapterException { boolean isVersioned = isVersioned(path); // We don't care if versioning is supported if we're not looking at a versioned path, // this also will short-circuit on isVersioned to prevent a costly call to // supportsVersioning // which gets proc to determine if the namespace is still versioned boolean supportsVersioning = isVersioned && getSelectedProfile().supportsVersioning(); // If the original path is a versioned path but the namespace no longer supports versioning if (isVersioned && !supportsVersioning) { // Chop off the versions prefix and the versioned file at the end path = path.substring(VERSION_PREFIX.length(), path.lastIndexOf('/') + 1); } return path; } /** * This is the asynchronous worker thread method where we: Call into setFileListToDirectory If * error, show an error If successful Update history Update panel Update right-click menu * * @param newPath * The new path we're changing to * @param oldPath * The old path we're changing from */ private void setPath(String newPath, final String oldPath) { ProfilePanelAdapter adapter = profileModel.getProfileAdapter(); try { ArcMoverDirectory dir = null; List<String> fileListColumns = adapter.getFileListColumns(); newPath = stripVersioningIfUnsupported(newPath); boolean isVersioned = isVersioned(newPath); if (isVersioned) { dir = adapter.getVersions(filePath(newPath)); fileListColumns = ((Hcp3AuthNamepaceProfilePanelAdapter) adapter).getVersionListColumns(); } else { dir = adapter.getDirectory(newPath); } setFileListToDirectory(dir, fileListColumns); AbstractProfileBase selectedProfile = profileModel.getSelectedProfile(); String[] paths = HistoryManager.addToHistory(selectedProfile, newPath); setCurrent(dir, newPath, paths, selectedProfile); } catch (Exception e) { String msg = "An error occurred reading directory " + profileModel.getSelectedProfile().decode(newPath); if (e instanceof StorageAdapterLiteralException) { msg = DBUtils.getErrorMessage(msg, e); } LOG.log(Level.WARNING, msg, e); GUIHelper.showMessageDialog(this, msg, "Error Reading Directory", JOptionPane.ERROR_MESSAGE); String[] paths = HistoryManager.removeFromHistory(profileModel.getSelectedProfile(), newPath); ArcMoverDirectory homeDir = null; boolean retreat = true; try { homeDir = adapter.getHomeDirectory(); } catch (Exception ie) { LOG.log(Level.WARNING, "Could not determine home directory", e); retreat = false; GUIHelper.invokeAndWait(new Runnable() { public void run() { profileSelectionCombo.setSelectedItem(FileSystemProfile.LOCAL_FILESYSTEM_PROFILE); } }, "change to the local file system profile"); } if (retreat) { final String homePath = homeDir.getPath(); if (oldPath.equals(newPath) && oldPath.equals(homePath)) { // Already home, go to LFS forceRefresh = true; GUIHelper.invokeAndWait(new Runnable() { public void run() { profileSelectionCombo.setSelectedItem(FileSystemProfile.LOCAL_FILESYSTEM_PROFILE); } }, "change to the local file system profile"); } else if (oldPath.equals(newPath)) { // Would just end up here anyway, go home and force us to go through the motions // This setCurrent prevents us from ping-ponging between homePath and newPath setCurrent(homeDir, homePath, paths, getSelectedProfile()); forceRefresh = true; // setCurrent cleans refresh GUIHelper.invokeAndWait(new Runnable() { public void run() { pathCombo.setSelectedItem(homePath); } }, "navigate to home directory"); } else { // Try retreating forceRefresh = true; GUIHelper.invokeAndWait(new Runnable() { public void run() { pathCombo.setSelectedItem(oldPath); } }, "return to previous directory"); } } } } class SetPathWorker extends SwingWorker { String newPath; String oldPath; public SetPathWorker(final String newPath, final String oldPath) { super(); this.newPath = newPath; this.oldPath = oldPath; } @Override protected void done() { unlock(); } @Override protected Object doInBackground() throws Exception { setPath(newPath, oldPath); return null; } } private class ProfileComboBoxModel extends AbstractListModel implements ComboBoxModel { private List<AbstractProfileBase> profiles; private AbstractProfileBase selectedProfile; private ProfilePanelAdapter selectedAdapter; // Using the Map below doesn't work for adapter. The hash code // for a profile is changing after it has been put in the map, causing the get on the map to // fail when in fact // the profile is in the map. So instead of using a Map, just keep a separate List of // adapters, indexed the same // private Map<AbstractProfileBase, ProfilePanelAdapter> adapters = new // HashMap<AbstractProfileBase, ProfilePanelAdapter>(); private List<ProfilePanelAdapter> adapters; public ProfileComboBoxModel() { loadProfiles(); } public void loadProfiles() { AbstractProfileBase oldProfile = selectedProfile; selectedProfile = null; selectedAdapter = null; profiles = ProfileManager.getProfiles(); profiles.add(0, FileSystemProfile.LOCAL_FILESYSTEM_PROFILE); adapters = new ArrayList<ProfilePanelAdapter>(profiles.size()); for (AbstractProfileBase profile : profiles) { ProfilePanelAdapter adapter = profile.makeAdapter(); adapters.add(adapter); if (profile.equals(oldProfile)) { selectedProfile = profile; selectedAdapter = adapter; } } if (selectedProfile == null) { selectedProfile = profiles.get(0); selectedAdapter = adapters.get(0); } setSelectedItem(selectedProfile); fireContentsChanged(this, 0, profiles.size()); } /** * Returns the length of the list. * * @return the length of the list */ public int getSize() { return profiles.size(); } /** * Returns the value at the specified index. * * @param index * the requested index * @return the value at <code>index</code> */ public Object getElementAt(int index) { return profiles.get(index); } /** * Set the selected item. The implementation of this method should notify all registered * <code>ListDataListener</code>s that the contents have changed. * * @param anItem * the list object to select or <code>null</code> to clear the selection */ public void setSelectedItem(Object anItem) { boolean foundProfile = false; for (int i = 0; i < profiles.size(); i++) { if (profiles.get(i).equals(anItem)) { selectedProfile = profiles.get(i); selectedAdapter = adapters.get(i); foundProfile = true; break; } } if (!foundProfile) { throw new RuntimeException("Profile " + anItem + " not found in list"); } } /** * Returns the selected item * * @return The selected item or <code>null</code> if there is no selection */ public Object getSelectedItem() { return selectedProfile; } public AbstractProfileBase getSelectedProfile() { return selectedProfile; } public ProfilePanelAdapter getProfileAdapter() { return selectedAdapter; } } }