ca.sqlpower.swingui.db.DatabaseConnectionManager.java Source code

Java tutorial

Introduction

Here is the source code for ca.sqlpower.swingui.db.DatabaseConnectionManager.java

Source

/*
 * Copyright (c) 2008, SQL Power Group Inc.
 *
 * This file is part of SQL Power Library.
 *
 * SQL Power Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SQL Power Library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */

package ca.sqlpower.swingui.db;

import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

import org.apache.log4j.Logger;

import ca.sqlpower.sql.DataSourceCollection;
import ca.sqlpower.sql.DatabaseListChangeEvent;
import ca.sqlpower.sql.DatabaseListChangeListener;
import ca.sqlpower.sql.JDBCDataSource;
import ca.sqlpower.sql.Olap4jDataSource;
import ca.sqlpower.sql.SPDataSource;
import ca.sqlpower.swingui.Messages;
import ca.sqlpower.swingui.SPSUtils;
import ca.sqlpower.swingui.table.CleanupTableModel;
import ca.sqlpower.swingui.table.EditableJTable;

import com.jgoodies.forms.builder.ButtonStackBuilder;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.debug.FormDebugPanel;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * The database connection manager is a GUI facility for managing a DataSourceCollection.
 * It allows users to add, edit, and delete database connection specs.
 */
public class DatabaseConnectionManager {

    private static Logger logger = Logger.getLogger(DatabaseConnectionManager.class);

    public static final Icon DB_ICON = new ImageIcon(DatabaseConnectionManager.class.getClassLoader()
            .getResource("ca/sqlpower/swingui/db/connection-db-16.png"));
    public static final Icon OLAP_DB_ICON = new ImageIcon(DatabaseConnectionManager.class.getClassLoader()
            .getResource("ca/sqlpower/swingui/db/connection-olap-16.png"));

    /**
     * A property key that can be set with a value for any additional actions passed into the
     * DatabaseConnectionManager constructor. If you set the Action to have a value of Boolean.TRUE
     * with this key, then the DatabaseConnectionManager will disable the corresponding button
     * it creates in the GUI for that action.
     */
    public static final String DISABLE_IF_NO_CONNECTION_SELECTED = "disableIfNoConnectionSelected"; //$NON-NLS-1$

    /**
     * A property key that can be set with a value for any additional actions passed into the
     * DatabaseConnectionManager constructor. This property can be set to SwingConstants values of
     * CENTER, TOP, or BOTTOM to define the text to be placed at the center, top, or bottom of it's button.
     */
    public static final String VERTICAL_TEXT_POSITION = "verticalTextPosition"; //$NON-NLS-1$

    /**
     * A property key that can be set with a value for any additional actions passed into the
     * DatabaseConnectionManager constructor. This property can be set to a default height
     * for the button.
     */
    public static final String ADDITIONAL_BUTTON_HEIGHT = "additionalButtonHeight"; //$NON-NLS-1$

    /**
     * A property key that can be set with a value for any additional actions passed into the
     * DatabaseConnectionManager constructor. This property can be set to SwingConstants values of
     * LEFT, RIGHT, CENTER, LEADING, or TRAILING.
     */
    public static final String HORIZONTAL_TEXT_POSITION = "horizontalTextPosition";

    /**
     * The GUI panel.  Lives inside the dialog {@link #d}.
     */
    private final JPanel panel;

    /**
     * The Dialog that contains all the GUI;
     */
    private JDialog d;

    /**
     * The current owner of the dialog.  Gets updated in the showDialog() method.
     */
    private Window currentOwner;

    private final DataSourceDialogFactory dsDialogFactory;

    private final DataSourceTypeDialogFactory dsTypeDialogFactory;

    /**
     * This tracks the icon currently placed in the table of connections to the
     * left of each database connection. Changing this icon will change the icon
     * displayed in the table. This defaults to an orange database icon.
     */
    private Icon dbIcon = DB_ICON;

    private final Action jdbcDriversAction = new AbstractAction(
            Messages.getString("DatabaseConnectionManager.jdbcDriversActionName")) { //$NON-NLS-1$

        public void actionPerformed(ActionEvent e) {

            // Was previously: dsTypeDialogFactory.showDialog((d != null) ? d : DatabaseConnectionManager.this.currentOwner);
            // However, this caused a bug where a ghost dialog was created after the jdbcDialog
            // was closed. The jdbcDialog is now owned by a different window, and this dialog can be selected out from under
            // it. This was the only way of having the jdbcDialog maintain its state between closings.

            dsTypeDialogFactory.showDialog(DatabaseConnectionManager.this.currentOwner);
        }
    };

    /**
     * This action will create a new data source and allow the user to decide
     * what kind of data source to create as well as allow the user to define
     * data source properties.
     */
    private class NewConnectionAction extends AbstractAction {

        /**
         * This component is used to define the position where the popup will
         * appear. It should be the same position where the given component is.
         */
        private final JComponent parentComponent;

        public NewConnectionAction(String name, JComponent parentComponent) {
            super(name);
            this.parentComponent = parentComponent;
        }

        public void actionPerformed(ActionEvent e) {
            if (creatableDSTypes.size() > 1) {
                final JPopupMenu dsTypeMenu = new JPopupMenu();
                dsTypeMenu.setLocation(parentComponent.getLocationOnScreen());
                Iterator<Class<? extends SPDataSource>> iterator = creatableDSTypes.iterator();
                while (iterator.hasNext()) {
                    final Class<? extends SPDataSource> dsType = iterator.next();
                    AbstractAction newDSAction = new AbstractAction(
                            SPDataSource.getUserFriendlyName(dsType) + "...") {
                        public void actionPerformed(ActionEvent e) {
                            showNewDSDialog(dsType);
                        }
                    };
                    JMenuItem dsItem = new JMenuItem(newDSAction);
                    if (dsType.equals(JDBCDataSource.class)) {
                        dsItem.setIcon(DB_ICON);
                    } else if (dsType.equals(Olap4jDataSource.class)) {
                        dsItem.setIcon(OLAP_DB_ICON);
                    }
                    dsTypeMenu.add(dsItem);
                }
                dsTypeMenu.show(parentComponent, 0, 0);
            } else {
                Iterator<Class<? extends SPDataSource>> iterator = creatableDSTypes.iterator();
                Class<? extends SPDataSource> dsTypeToCreate = iterator.next();
                showNewDSDialog(dsTypeToCreate);
            }

        }

        public void showNewDSDialog(Class<? extends SPDataSource> dsTypeToCreate) {
            if (dsTypeToCreate.equals(JDBCDataSource.class)) {
                final JDBCDataSource ds = new JDBCDataSource(getPlDotIni());
                Runnable onOk = new Runnable() {
                    public void run() {
                        dsCollection.addDataSource(ds);
                        dsTable.updateUI();
                    }
                };
                dsDialogFactory.showDialog((d != null) ? d : DatabaseConnectionManager.this.currentOwner, ds, onOk);
            } else if (dsTypeToCreate.equals(Olap4jDataSource.class)) {
                final Olap4jDataSource ds = new Olap4jDataSource(getPlDotIni());
                Runnable onOk = new Runnable() {
                    public void run() {
                        dsCollection.addDataSource(ds);
                        dsTable.updateUI();
                    }
                };
                dsDialogFactory.showDialog((d != null) ? d : DatabaseConnectionManager.this.currentOwner, ds,
                        getPlDotIni(), onOk);
            } else {
                throw new IllegalStateException("Cannot make a new data source of type " + dsTypeToCreate);
            }
        }
    }

    private final Action editDatabaseConnectionAction = new AbstractAction(
            Messages.getString("DatabaseConnectionManager.editDbConnectionActionName")) { //$NON-NLS-1$

        public void actionPerformed(ActionEvent e) {
            int selectedRow = dsTable.getSelectedRow();
            if (selectedRow == -1) {
                return;
            }
            final SPDataSource ds = (SPDataSource) dsTable.getValueAt(selectedRow, 0);

            if (ds instanceof JDBCDataSource) {
                JDBCDataSource jdbcDS = (JDBCDataSource) ds;
                Runnable onOk = createOnOk(ds);

                dsDialogFactory.showDialog((d != null) ? d : DatabaseConnectionManager.this.currentOwner, jdbcDS,
                        onOk);
            } else if (ds instanceof Olap4jDataSource) {
                Olap4jDataSource jdbcDS = (Olap4jDataSource) ds;
                Runnable onOk = createOnOk(ds);

                dsDialogFactory.showDialog((d != null) ? d : DatabaseConnectionManager.this.currentOwner, jdbcDS,
                        getPlDotIni(), onOk);
            } else {
                throw new IllegalStateException(
                        "Unknown SPDataSource type in the connection manager. Type is " + ds.getClass());
            }
        }

        private Runnable createOnOk(final SPDataSource ds) {
            Runnable onOk = new Runnable() {
                public void run() {
                    try {
                        for (int i = 0; i < dsTable.getRowCount(); i++) {
                            if (dsTable.getValueAt(i, 0) == ds) {
                                dsTable.setRowSelectionInterval(i, i);
                                dsTable.scrollRectToVisible(dsTable.getCellRect(i, 0, true));
                                dsTable.repaint();
                                break;
                            }
                        }
                    } catch (Exception ex) {
                        SPSUtils.showExceptionDialogNoReport(
                                (d != null) ? d : DatabaseConnectionManager.this.currentOwner,
                                "Unexpected exception while editing a database connection.", //$NON-NLS-1$
                                ex);
                    }
                }
            };
            return onOk;
        }
    };

    private final Action removeDatabaseConnectionAction = new AbstractAction(
            Messages.getString("DatabaseConnectionManager.removeDbConnectionActionName")) { //$NON-NLS-1$

        public void actionPerformed(ActionEvent e) {
            int selectedRow = dsTable.getSelectedRow();
            if (selectedRow == -1) {
                return;
            }
            SPDataSource dbcs = (SPDataSource) dsTable.getValueAt(selectedRow, 0);
            int option = JOptionPane.showConfirmDialog(
                    (d != null) ? d : DatabaseConnectionManager.this.currentOwner,
                    Messages.getString("DatabaseConnectionManager.deleteDbConnectionConfirmation", dbcs.getName()), //$NON-NLS-1$
                    Messages.getString("DatabaseConnectionManager.removeButton"), //$NON-NLS-1$
                    JOptionPane.YES_NO_OPTION);
            if (option != JOptionPane.YES_OPTION) {
                return;
            }

            dsCollection.removeDataSource(dbcs);
            dsTable.clearSelection();

            for (JButton b : additionalActionButtons) {
                Object disableValue = b.getAction().getValue(DISABLE_IF_NO_CONNECTION_SELECTED);
                if (disableValue instanceof Boolean && disableValue.equals(Boolean.TRUE)) {
                    b.setEnabled(false);
                }
            }
            removeDatabaseConnectionAction.setEnabled(false);
            editDatabaseConnectionAction.setEnabled(false);

            dsTable.repaint();

        }
    };

    private final Action closeAction = new AbstractAction(
            Messages.getString("DatabaseConnectionManager.closeActionName")) { //$NON-NLS-1$
        public void actionPerformed(ActionEvent e) {
            d.dispose();
        }
    };

    /**
     * The table that contains the list of all data sources in the
     * user's collection of data sources.
     */
    private JTable dsTable;

    /**
     * The data source collection of the session context this connection
     * manager belongs to.
     */
    private final DataSourceCollection<SPDataSource> dsCollection;

    private List<JButton> additionalActionButtons = new ArrayList<JButton>();

    /**
     * This list contains all of the classes that are able to be created from
     * the new button.
     */
    private List<Class<? extends SPDataSource>> creatableDSTypes;

    /**
     * Creates a new database connection manager with the default data source
     * and data source type dialog factories.
     * 
     * @param dsCollection The data source collection to manage
     */
    public DatabaseConnectionManager(DataSourceCollection<SPDataSource> dsCollection) {
        this(dsCollection, new DefaultDataSourceDialogFactory(),
                new DefaultDataSourceTypeDialogFactory(dsCollection), (List<Action>) Collections.EMPTY_LIST);
    }

    /**
     * This constructor allows defining a parent window to start and gives the option to hide the
     * close button. The main purpose of using this constructor would be to make a db connection
     * manager that is to be placed in another panel.
     */
    public DatabaseConnectionManager(DataSourceCollection<SPDataSource> dsCollection,
            DataSourceDialogFactory dsDialogFactory, DataSourceTypeDialogFactory dsTypeDialogFactory,
            List<Action> additionalActions, List<JComponent> additionalComponents, Window owner,
            boolean showCloseButton) {
        this(dsCollection, dsDialogFactory, dsTypeDialogFactory, additionalActions, additionalComponents, owner,
                showCloseButton, new ArrayList<Class<? extends SPDataSource>>(
                        Collections.singleton((Class<? extends SPDataSource>) JDBCDataSource.class)));
    }

    /**
     * Using this constructor over the other available constructors allows
     * defining a connection manager that can create {@link SPDataSource} types
     * other than the default {@link JDBCDataSource} type. If a constructor is
     * used other then this one only new {@link JDBCDataSource} types will be
     * able to be constructed although any {@link SPDataSource} type in the list
     * will be editable.
     */
    public DatabaseConnectionManager(DataSourceCollection<SPDataSource> dsCollection,
            DataSourceDialogFactory dsDialogFactory, DataSourceTypeDialogFactory dsTypeDialogFactory,
            List<Action> additionalActions, List<JComponent> additionalComponents, Window owner,
            boolean showCloseButton, List<Class<? extends SPDataSource>> dsTypes) {
        this.dsCollection = dsCollection;
        this.dsDialogFactory = dsDialogFactory;
        this.dsTypeDialogFactory = dsTypeDialogFactory;
        logger.debug("Window owner is " + owner);
        currentOwner = owner;
        panel = createPanel(additionalActions, additionalComponents, showCloseButton,
                Messages.getString("DatabaseConnectionManager.availableDbConnections"));
        creatableDSTypes = new ArrayList<Class<? extends SPDataSource>>(dsTypes);
    }

    /**
     * Creates a new database connection manager with the default set of action buttons, plus
     * those supplied in the given list.
     */
    public DatabaseConnectionManager(DataSourceCollection<SPDataSource> dsCollection,
            DataSourceDialogFactory dsDialogFactory, DataSourceTypeDialogFactory dsTypeDialogFactory,
            List<Action> additionalActions) {
        this(dsCollection, dsDialogFactory, dsTypeDialogFactory, additionalActions, new ArrayList<JComponent>(),
                null, true);
    }

    /**
     * Creates a new database connection manager with the default set of action buttons.
     * 
     * @param dsCollection The data source collection to manage
     * @param dsDialogFactory The factory that this manager will use to create all DataSource editor dialogs.
     */
    @SuppressWarnings("unchecked")
    public DatabaseConnectionManager(DataSourceCollection dsCollection, DataSourceDialogFactory dsDialogFactory,
            DataSourceTypeDialogFactory dsTypeDialogFactory) {
        this(dsCollection, dsDialogFactory, dsTypeDialogFactory, Collections.EMPTY_LIST);
    }

    /**
     * Makes sure this database connection manager is visible,
     * focused, and in a dialog owned by the given owner.
     *
     * @param owner the Frame or Dialog that should own the
     *              DatabaseConnectionManager dialog.
     */
    public void showDialog(Window owner) {
        if (d != null && d.isVisible() && currentOwner == owner) {
            d.setVisible(true); // even if the dialog is already visible, this brings it to the front and gives it focus
            d.requestFocus(); // this will rob focus from the previous focus owner
            return;
        }

        if (d != null) {
            d.dispose();
        }
        if (panel.getParent() != null) {
            panel.getParent().remove(panel);
        }
        if (owner instanceof Dialog) {
            d = new JDialog((Dialog) owner);
        } else if (owner instanceof Frame) {
            d = new JDialog((Frame) owner);
        } else {
            throw new IllegalArgumentException("Owner has to be a Frame or Dialog.  You provided a " + //$NON-NLS-1$
                    (owner == null ? null : owner.getClass().getName()));
        }

        currentOwner = owner;
        d.setTitle(Messages.getString("DatabaseConnectionManager.dialogTitle")); //$NON-NLS-1$
        d.getContentPane().add(panel);
        d.pack();
        d.setLocationRelativeTo(owner);
        SPSUtils.makeJDialogCancellable(d, closeAction);
        d.setVisible(true);
        d.requestFocus();
    }

    /**
     * Closes the current dialog. It is safe to call this even if the dialog is not visible.
     */
    public void closeDialog() {
        if (d != null) {
            d.dispose();
        }
    }

    public void setDbIcon(Icon dbIcon) {
        this.dbIcon = dbIcon;
    }

    /**
     * This method returns the main panel in the database connection manager and additionally sets the dialog to be one
     * that can be passed in. This is required for loading a project when a data source cannot be found. Wabit needs
     * to pop up a window giving the user an option to skip the datasource, select a datasource or cancel the load
     * and therefore this method can be used to create the proper panel and give it the proper parent so that it can
     * then pop up dialogs.
     */
    public JPanel createPanelStandalone(List<Action> additionalActions, List<JComponent> additionalComponents,
            boolean showCloseButton, String message, JDialog owner) {
        d = owner;
        return createPanel(additionalActions, additionalComponents, showCloseButton, message);
    }

    private JPanel createPanel(List<Action> additionalActions, List<JComponent> additionalComponents,
            boolean showCloseButton, String message) {

        FormLayout layout = new FormLayout("6dlu, fill:min(160dlu;default):grow, 6dlu, pref, 6dlu", // columns //$NON-NLS-1$
                " 6dlu,10dlu,6dlu,fill:min(180dlu;default):grow,10dlu"); // rows //$NON-NLS-1$

        layout.setColumnGroups(new int[][] { { 1, 3, 5 } });
        CellConstraints cc = new CellConstraints();

        PanelBuilder pb;
        JPanel p = logger.isDebugEnabled() ? new FormDebugPanel(layout) : new JPanel(layout);
        pb = new PanelBuilder(layout, p);
        pb.setDefaultDialogBorder();

        pb.add(new JLabel(message), cc.xyw(2, 2, 3)); //$NON-NLS-1$

        TableModel tm = new ConnectionTableModel(dsCollection);
        dsTable = new EditableJTable(tm);
        dsTable.setTableHeader(null);
        dsTable.setShowGrid(false);
        dsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        dsTable.addMouseListener(new DSTableMouseListener());
        dsTable.setDefaultRenderer(SPDataSource.class, new ConnectionTableCellRenderer());

        JScrollPane sp = new JScrollPane(dsTable);
        sp.getViewport().setBackground(dsTable.getBackground());

        pb.add(sp, cc.xy(2, 4));

        ButtonStackBuilder bsb = new ButtonStackBuilder();

        JButton newButton = new JButton();
        AbstractAction newDatabaseConnectionAction = new NewConnectionAction(
                Messages.getString("DatabaseConnectionManager.newDbConnectionActionName"), newButton); //$NON-NLS-1$
        newButton.setAction(newDatabaseConnectionAction);
        bsb.addGridded(newButton);
        bsb.addRelatedGap();
        bsb.addGridded(new JButton(editDatabaseConnectionAction));
        bsb.addRelatedGap();
        bsb.addGridded(new JButton(removeDatabaseConnectionAction));

        removeDatabaseConnectionAction.setEnabled(false);
        editDatabaseConnectionAction.setEnabled(false);

        bsb.addUnrelatedGap();
        JButton jdbcDriversButton = new JButton(jdbcDriversAction);
        bsb.addGridded(jdbcDriversButton);

        for (Action a : additionalActions) {
            bsb.addUnrelatedGap();
            JButton b = new JButton(a);
            Object disableValue = a.getValue(DISABLE_IF_NO_CONNECTION_SELECTED);
            if (disableValue instanceof Boolean && disableValue.equals(Boolean.TRUE)) {
                b.setEnabled(false);
            }

            Object heightValue = a.getValue(ADDITIONAL_BUTTON_HEIGHT);
            if (heightValue instanceof Integer) {
                b.setPreferredSize(new Dimension((int) b.getPreferredSize().getWidth(), (Integer) heightValue));
            }

            Object verticalTextPos = a.getValue(VERTICAL_TEXT_POSITION);
            if (verticalTextPos instanceof Integer) {
                Integer verticalTextInt = (Integer) verticalTextPos;
                if (verticalTextInt == SwingConstants.TOP || verticalTextInt == SwingConstants.BOTTOM
                        || verticalTextInt == SwingConstants.CENTER) {
                    b.setVerticalTextPosition(verticalTextInt);
                }
            }

            Object horizontalTextPos = a.getValue(HORIZONTAL_TEXT_POSITION);
            if (horizontalTextPos instanceof Integer) {
                Integer horizontalTextInt = (Integer) horizontalTextPos;
                if (horizontalTextInt == SwingConstants.LEFT || horizontalTextInt == SwingConstants.RIGHT
                        || horizontalTextInt == SwingConstants.CENTER || horizontalTextInt == SwingConstants.LEADING
                        || horizontalTextInt == SwingConstants.TRAILING) {
                    b.setHorizontalTextPosition(horizontalTextInt);
                }
            }

            additionalActionButtons.add(b);
            bsb.addFixed(b);
        }

        for (JComponent comp : additionalComponents) {
            bsb.addUnrelatedGap();
            bsb.addFixed(comp);
        }

        if (showCloseButton) {
            bsb.addUnrelatedGap();
            bsb.addGridded(new JButton(closeAction));
        }

        pb.add(bsb.getPanel(), cc.xy(4, 4));
        return pb.getPanel();

    }

    private static class ConnectionTableModel extends AbstractTableModel implements CleanupTableModel {

        private final DatabaseListChangeListener databaseListChangeListener = new DatabaseListChangeListener() {
            public void databaseAdded(DatabaseListChangeEvent e) {
                fireTableDataChanged();
            }

            public void databaseRemoved(DatabaseListChangeEvent e) {
                fireTableDataChanged();
            }
        };

        private final DataSourceCollection<SPDataSource> dsCollection;

        public ConnectionTableModel(DataSourceCollection<SPDataSource> dsCollection) {
            super();
            this.dsCollection = dsCollection;
            dsCollection.addDatabaseListChangeListener(databaseListChangeListener);
        }

        public int getRowCount() {
            return dsCollection.getConnections().size();
        }

        public int getColumnCount() {
            return 1;
        }

        @Override
        public String getColumnName(int columnIndex) {
            return Messages.getString("DatabaseConnectionManager.connectionName"); //$NON-NLS-1$
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return SPDataSource.class;
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return false;
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            return dsCollection.getConnections().get(rowIndex);
        }

        public void cleanup() {
            dsCollection.removeDatabaseListChangeListener(databaseListChangeListener);
        }

    }

    private class ConnectionTableCellRenderer implements TableCellRenderer {

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            Component comp = new DefaultTableCellRenderer().getTableCellRendererComponent(table, value, isSelected,
                    hasFocus, row, column);
            if (comp instanceof JLabel) {
                if (value instanceof JDBCDataSource) {
                    ((JLabel) comp).setIcon(dbIcon);
                } else if (value instanceof Olap4jDataSource) {
                    ((JLabel) comp).setIcon(OLAP_DB_ICON);
                }
            }
            return comp;
        }

    }

    public DataSourceCollection<SPDataSource> getPlDotIni() {
        return dsCollection;
    }

    private class DSTableMouseListener implements MouseListener {

        /**
         * Updates the state of all buttons when a database connection is clicked on
         * @param evt  Mouse event
         */
        private void updateAllButtonStates(MouseEvent evt) {
            for (JButton b : additionalActionButtons) {
                Object disableValue = b.getAction().getValue(DISABLE_IF_NO_CONNECTION_SELECTED);
                if (disableValue instanceof Boolean && disableValue.equals(Boolean.TRUE)) {
                    if (getSelectedConnection() == null) {
                        b.setEnabled(false);
                    } else {
                        b.setEnabled(true);
                    }
                }
            }

            if (getSelectedConnection() == null) {
                removeDatabaseConnectionAction.setEnabled(false);
                editDatabaseConnectionAction.setEnabled(false);
            } else {
                removeDatabaseConnectionAction.setEnabled(true);
                editDatabaseConnectionAction.setEnabled(true);
            }
        }

        public void mouseClicked(MouseEvent evt) {
            if (evt.getClickCount() == 2) {
                editDatabaseConnectionAction.actionPerformed(null);
            }
        }

        public void mousePressed(MouseEvent evt) {
            updateAllButtonStates(evt);
        }

        public void mouseReleased(MouseEvent evt) {
            updateAllButtonStates(evt);
        }

        public void mouseEntered(MouseEvent e) {
            // we don't care
        }

        public void mouseExited(MouseEvent e) {
            // we don't care
        }

    }

    /**
     * Returns the first selected spdatasource object from the list.
     * Returns null if there are not any selected data sources
     */
    public SPDataSource getSelectedConnection() {
        int selectedRow = dsTable.getSelectedRow();
        if (selectedRow == -1) {
            return null;
        }
        return (SPDataSource) dsTable.getValueAt(selectedRow, 0);
    }

    /**
     * This will return the database connection manager as a panel 
     * so it can be placed in other panels rather than appearing
     * in its own window.
     */
    public JPanel getPanel() {
        return panel;
    }

}