org.eobjects.datacleaner.panels.WelcomePanel.java Source code

Java tutorial

Introduction

Here is the source code for org.eobjects.datacleaner.panels.WelcomePanel.java

Source

/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Neopost - Customer Information Management
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.eobjects.datacleaner.panels;

import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;

import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.eobjects.analyzer.configuration.AnalyzerBeansConfiguration;
import org.eobjects.analyzer.connection.AccessDatastore;
import org.eobjects.analyzer.connection.CouchDbDatastore;
import org.eobjects.analyzer.connection.CsvDatastore;
import org.eobjects.analyzer.connection.Datastore;
import org.eobjects.analyzer.connection.DatastoreConnection;
import org.eobjects.analyzer.connection.DbaseDatastore;
import org.eobjects.analyzer.connection.ExcelDatastore;
import org.eobjects.analyzer.connection.FixedWidthDatastore;
import org.eobjects.analyzer.connection.HBaseDatastore;
import org.eobjects.analyzer.connection.JdbcDatastore;
import org.eobjects.analyzer.connection.JsonDatastore;
import org.eobjects.analyzer.connection.MongoDbDatastore;
import org.eobjects.analyzer.connection.OdbDatastore;
import org.eobjects.analyzer.connection.SalesforceDatastore;
import org.eobjects.analyzer.connection.SasDatastore;
import org.eobjects.analyzer.connection.SugarCrmDatastore;
import org.eobjects.analyzer.connection.XmlDatastore;
import org.eobjects.analyzer.util.StringUtils;
import org.eobjects.datacleaner.actions.OpenAnalysisJobActionListener;
import org.eobjects.datacleaner.database.DatabaseDriverCatalog;
import org.eobjects.datacleaner.database.DatabaseDriverDescriptor;
import org.eobjects.datacleaner.guice.InjectorBuilder;
import org.eobjects.datacleaner.user.DatastoreChangeListener;
import org.eobjects.datacleaner.user.MutableDatastoreCatalog;
import org.eobjects.datacleaner.user.UserPreferences;
import org.eobjects.datacleaner.util.DCDocumentListener;
import org.eobjects.datacleaner.util.IconUtils;
import org.eobjects.datacleaner.util.ImageManager;
import org.eobjects.datacleaner.util.WidgetFactory;
import org.eobjects.datacleaner.util.WidgetUtils;
import org.eobjects.datacleaner.widgets.Alignment;
import org.eobjects.datacleaner.widgets.DCLabel;
import org.eobjects.datacleaner.widgets.DCPopupBubble;
import org.eobjects.datacleaner.widgets.OpenAnalysisJobMenuItem;
import org.eobjects.datacleaner.windows.AbstractDialog;
import org.eobjects.datacleaner.windows.AccessDatastoreDialog;
import org.eobjects.datacleaner.windows.AnalysisJobBuilderWindow;
import org.eobjects.datacleaner.windows.CompositeDatastoreDialog;
import org.eobjects.datacleaner.windows.CouchDbDatastoreDialog;
import org.eobjects.datacleaner.windows.CsvDatastoreDialog;
import org.eobjects.datacleaner.windows.DbaseDatastoreDialog;
import org.eobjects.datacleaner.windows.ExcelDatastoreDialog;
import org.eobjects.datacleaner.windows.FixedWidthDatastoreDialog;
import org.eobjects.datacleaner.windows.HBaseDatastoreDialog;
import org.eobjects.datacleaner.windows.JdbcDatastoreDialog;
import org.eobjects.datacleaner.windows.JsonDatastoreDialog;
import org.eobjects.datacleaner.windows.MongoDbDatastoreDialog;
import org.eobjects.datacleaner.windows.OdbDatastoreDialog;
import org.eobjects.datacleaner.windows.OptionsDialog;
import org.eobjects.datacleaner.windows.SalesforceDatastoreDialog;
import org.eobjects.datacleaner.windows.SasDatastoreDialog;
import org.eobjects.datacleaner.windows.SugarCrmDatastoreDialog;
import org.eobjects.datacleaner.windows.XmlDatastoreDialog;
import org.jdesktop.swingx.JXTextField;
import org.jdesktop.swingx.VerticalLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Injector;

/**
 * Panel to select which job or datastore to use. Shown in the "source" tab, if
 * no datastore or job has been selected to begin with.
 */
public class WelcomePanel extends DCPanel implements DatastoreChangeListener {

    private static final long serialVersionUID = 1L;

    private Logger logger = LoggerFactory.getLogger(WelcomePanel.class);

    private static final ImageManager imageManager = ImageManager.get();

    private static final int MAX_JOB_PANELS = 3;

    private final MutableDatastoreCatalog _datastoreCatalog;
    private final AnalysisJobBuilderWindow _analysisJobBuilderWindow;
    private final Provider<OptionsDialog> _optionsDialogProvider;
    private final DatabaseDriverCatalog _databaseDriverCatalog;
    private final List<DatastorePanel> _datastorePanels;
    private final DCGlassPane _glassPane;
    private final JButton _analyzeButton;
    private final JButton _browseJobsButton;
    private final JButton _moreRecentJobsButton;
    private final DCPanel _datastoreListPanel;
    private final DCPanel _jobsListPanel;
    private final JXTextField _searchDatastoreTextField;
    private final InjectorBuilder _injectorBuilder;
    private final UserPreferences _userPreferences;
    private final AnalyzerBeansConfiguration _configuration;
    private final OpenAnalysisJobActionListener _openAnalysisJobActionListener;

    @Inject
    protected WelcomePanel(AnalyzerBeansConfiguration configuration,
            AnalysisJobBuilderWindow analysisJobBuilderWindow, DCGlassPane glassPane,
            Provider<OptionsDialog> optionsDialogProvider, InjectorBuilder injectorBuilder,
            OpenAnalysisJobActionListener openAnalysisJobActionListener,
            DatabaseDriverCatalog databaseDriverCatalog, UserPreferences userPreferences) {
        super();
        _openAnalysisJobActionListener = openAnalysisJobActionListener;
        _configuration = configuration;
        _datastorePanels = new ArrayList<DatastorePanel>();
        _datastoreCatalog = (MutableDatastoreCatalog) configuration.getDatastoreCatalog();
        _analysisJobBuilderWindow = analysisJobBuilderWindow;
        _glassPane = glassPane;
        _optionsDialogProvider = optionsDialogProvider;
        _injectorBuilder = injectorBuilder;
        _databaseDriverCatalog = databaseDriverCatalog;
        _userPreferences = userPreferences;

        _browseJobsButton = new JButton("Browse jobs",
                imageManager.getImageIcon(IconUtils.MENU_OPEN, IconUtils.ICON_SIZE_SMALL));
        _browseJobsButton.setMargin(new Insets(1, 1, 1, 4));
        _browseJobsButton.addActionListener(openAnalysisJobActionListener);

        // initialize "analyze" button
        _analyzeButton = new JButton("Build job",
                imageManager.getImageIcon(IconUtils.MODEL_JOB, IconUtils.ICON_SIZE_SMALL));
        _analyzeButton.setMargin(new Insets(1, 1, 1, 1));
        _analyzeButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (DatastorePanel datastorePanel : _datastorePanels) {
                    if (datastorePanel.isSelected()) {
                        Datastore datastore = datastorePanel.getDatastore();

                        // open the connection here, to make any connection
                        // issues apparent early
                        try (DatastoreConnection datastoreConnection = datastore.openConnection()) {
                            datastoreConnection.getDataContext().getSchemaNames();
                            _analysisJobBuilderWindow.setDatastore(datastore);
                        }
                        return;
                    }
                }
            }
        });

        // initialize search text field
        _searchDatastoreTextField = WidgetFactory.createTextField("Search/filter datastores");
        _searchDatastoreTextField
                .setBorder(new CompoundBorder(new EmptyBorder(4, 0, 0, 0), WidgetUtils.BORDER_THIN));
        _searchDatastoreTextField.setOpaque(false);
        _searchDatastoreTextField.getDocument().addDocumentListener(new DCDocumentListener() {
            @Override
            protected void onChange(DocumentEvent event) {
                String text = _searchDatastoreTextField.getText();
                if (StringUtils.isNullOrEmpty(text)) {
                    // when there is no search query, set all datastores
                    // visible
                    for (DatastorePanel datastorePanel : _datastorePanels) {
                        datastorePanel.setVisible(true);
                    }
                } else {
                    // do a case insensitive search
                    text = text.trim().toLowerCase();
                    for (DatastorePanel datastorePanel : _datastorePanels) {
                        String name = datastorePanel.getDatastore().getName().toLowerCase();
                        datastorePanel.setVisible(name.indexOf(text) != -1);
                    }
                    selectFirstVisibleDatastore();
                }
            }
        });
        _searchDatastoreTextField.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    clickAnalyzeButton();
                } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                    selectNextVisibleDatastore();
                } else if (e.getKeyCode() == KeyEvent.VK_UP) {
                    selectPreviousVisibleDatastore();
                }
            }
        });

        setLayout(new VerticalLayout(4));

        add(Box.createVerticalStrut(10));

        final DCLabel jobsHeaderLabel = DCLabel.dark("Jobs");
        jobsHeaderLabel.setFont(WidgetUtils.FONT_HEADER1);
        add(jobsHeaderLabel);

        _jobsListPanel = new DCPanel();
        final GridLayout jobsListLayout = new GridLayout(1, MAX_JOB_PANELS);
        jobsListLayout.setHgap(10);
        _jobsListPanel.setBorder(new EmptyBorder(10, 10, 4, 0));
        _jobsListPanel.setLayout(jobsListLayout);

        final List<FileObject> recentJobFiles = getRecentJobFiles();

        updateJobsListPanel(recentJobFiles);

        add(_jobsListPanel);

        _moreRecentJobsButton = new JButton("More",
                imageManager.getImageIcon(IconUtils.FILE_FOLDER, IconUtils.ICON_SIZE_SMALL));
        _moreRecentJobsButton.setMargin(new Insets(1, 1, 1, 4));
        if (recentJobFiles.size() <= MAX_JOB_PANELS) {
            _moreRecentJobsButton.setEnabled(false);
        } else {
            _moreRecentJobsButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    final JPopupMenu popup = new JPopupMenu();
                    for (int i = 3; i < recentJobFiles.size(); i++) {
                        final FileObject jobFile = recentJobFiles.get(i);
                        final JMenuItem menuItem = new OpenAnalysisJobMenuItem(jobFile,
                                _openAnalysisJobActionListener);
                        popup.add(menuItem);
                    }
                    popup.show(_moreRecentJobsButton, 0, _moreRecentJobsButton.getHeight());
                }
            });
        }

        final DCPanel jobsButtonPanel = new DCPanel();
        jobsButtonPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0));
        jobsButtonPanel.add(_moreRecentJobsButton);
        jobsButtonPanel.add(_browseJobsButton);
        add(jobsButtonPanel);

        add(Box.createVerticalStrut(40));

        final DCLabel datastoreHeaderLabel = DCLabel.dark("Datastores");
        datastoreHeaderLabel.setFont(WidgetUtils.FONT_HEADER1);
        add(datastoreHeaderLabel);

        final DCLabel registerNewDatastoreLabel = DCLabel.dark("Register new:");
        registerNewDatastoreLabel.setFont(WidgetUtils.FONT_HEADER2);

        final DCPanel newDatastorePanel = new DCPanel();
        newDatastorePanel.setLayout(new VerticalLayout(4));
        newDatastorePanel.setBorder(new EmptyBorder(10, 10, 10, 0));
        newDatastorePanel.add(registerNewDatastoreLabel);
        newDatastorePanel.add(createNewDatastorePanel());

        add(newDatastorePanel);

        _datastoreListPanel = new DCPanel();
        _datastoreListPanel.setLayout(new VerticalLayout(4));
        _datastoreListPanel.setBorder(new EmptyBorder(10, 10, 4, 0));
        add(_datastoreListPanel);
        updateDatastores();

        final DCPanel datastoresButtonPanel = new DCPanel();
        datastoresButtonPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        datastoresButtonPanel.setBorder(new EmptyBorder(0, 10, 0, 0));
        datastoresButtonPanel.add(_analyzeButton);
        add(datastoresButtonPanel);
    }

    private List<FileObject> getRecentJobFiles() {
        final List<FileObject> recentJobFiles = _userPreferences.getRecentJobFiles();
        final List<FileObject> result = new ArrayList<>();
        for (FileObject fileObject : recentJobFiles) {
            try {
                if (fileObject.exists()) {
                    result.add(fileObject);
                    if (result.size() == 10) {
                        break;
                    }
                }
            } catch (FileSystemException ex) {
                logger.debug("Skipping file {} because of unexpected error", ex);
            }
        }
        return result;
    }

    private void updateJobsListPanel(List<FileObject> recentJobFiles) {
        if (recentJobFiles.isEmpty()) {
            _jobsListPanel.add(DCLabel.dark("(no recent jobs)"));
        } else {
            int jobIndex = 0;
            for (FileObject fileObject : recentJobFiles) {
                _jobsListPanel
                        .add(new OpenAnalysisJobPanel(fileObject, _configuration, _openAnalysisJobActionListener));
                jobIndex++;
                if (jobIndex == MAX_JOB_PANELS) {
                    break;
                }
            }
        }
    }

    private void updateDatastores() {
        Datastore selectedDatastore = getSelectedDatastore();
        _datastoreListPanel.removeAll();
        _datastorePanels.clear();

        final DCLabel existingDatastoresLabel = DCLabel.dark("Existing datastores:");
        existingDatastoresLabel.setFont(WidgetUtils.FONT_HEADER2);

        final DCPanel searchDatastorePanel = DCPanel.around(_searchDatastoreTextField);
        searchDatastorePanel.setBorder(WidgetUtils.BORDER_SHADOW);

        final DCPanel headerPanel = new DCPanel();
        headerPanel.setLayout(new FlowLayout(Alignment.LEFT.getFlowLayoutAlignment(), 0, 0));
        headerPanel.add(existingDatastoresLabel);
        headerPanel.add(Box.createHorizontalStrut(20));
        headerPanel.add(searchDatastorePanel);

        _datastoreListPanel.add(headerPanel);

        boolean selectFirst = true;

        String[] datastoreNames = _datastoreCatalog.getDatastoreNames();
        for (int i = 0; i < datastoreNames.length; i++) {
            final Datastore datastore = _datastoreCatalog.getDatastore(datastoreNames[i]);
            DatastorePanel datastorePanel = new DatastorePanel(datastore, _datastoreCatalog, this,
                    _analysisJobBuilderWindow.getWindowContext(), _injectorBuilder);
            _datastorePanels.add(datastorePanel);
            _datastoreListPanel.add(datastorePanel);

            if (selectedDatastore != null && selectedDatastore.getName().equals(datastore.getName())) {
                selectFirst = false;
                setSelectedDatastore(datastore);
            }
        }

        if (selectFirst) {
            selectFirstVisibleDatastore();
        }
    }

    public void setSelectedDatastore(Datastore datastore) {
        if (datastore != null) {
            for (DatastorePanel panel : _datastorePanels) {
                if (datastore.equals(panel.getDatastore())) {
                    panel.setSelected(true);
                } else {
                    panel.setSelected(false);
                }
            }
        }
    }

    public Datastore getSelectedDatastore() {
        DatastorePanel datastorePanel = getSelectedDatastorePanel();
        if (datastorePanel == null) {
            return null;
        }
        return datastorePanel.getDatastore();
    }

    private DCPanel createNewDatastorePanel() {
        final DCPanel panel = new DCPanel();
        panel.setBorder(WidgetUtils.BORDER_LIST_ITEM);
        panel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10));
        panel.add(createNewDatastoreButton("CSV file",
                "Comma-separated values (CSV) file (or file with other separators)", IconUtils.CSV_IMAGEPATH,
                CsvDatastore.class, CsvDatastoreDialog.class));
        panel.add(createNewDatastoreButton("Excel spreadsheet",
                "Microsoft Excel spreadsheet. Either .xls (97-2003) or .xlsx (2007+) format.",
                IconUtils.EXCEL_IMAGEPATH, ExcelDatastore.class, ExcelDatastoreDialog.class));
        panel.add(createNewDatastoreButton("Access database", "Microsoft Access database file (.mdb).",
                IconUtils.ACCESS_IMAGEPATH, AccessDatastore.class, AccessDatastoreDialog.class));
        panel.add(createNewDatastoreButton("SAS library", "A directory of SAS library files (.sas7bdat).",
                IconUtils.SAS_IMAGEPATH, SasDatastore.class, SasDatastoreDialog.class));
        panel.add(createNewDatastoreButton("DBase database", "DBase database file (.dbf)",
                IconUtils.DBASE_IMAGEPATH, DbaseDatastore.class, DbaseDatastoreDialog.class));
        panel.add(createNewDatastoreButton("Fixed width file",
                "Text file with fixed width values. Each value spans a fixed amount of text characters.",
                IconUtils.FIXEDWIDTH_IMAGEPATH, FixedWidthDatastore.class, FixedWidthDatastoreDialog.class));
        panel.add(createNewDatastoreButton("XML file", "Extensible Markup Language file (.xml)",
                IconUtils.XML_IMAGEPATH, XmlDatastore.class, XmlDatastoreDialog.class));
        panel.add(createNewDatastoreButton("JSON file", "JavaScript Object NOtation file (.json).",
                IconUtils.JSON_IMAGEPATH, JsonDatastore.class, JsonDatastoreDialog.class));
        panel.add(
                createNewDatastoreButton("OpenOffice.org Base database", "OpenOffice.org Base database file (.odb)",
                        IconUtils.ODB_IMAGEPATH, OdbDatastore.class, OdbDatastoreDialog.class));

        panel.add(Box.createHorizontalStrut(10));

        panel.add(createNewDatastoreButton("Salesforce.com", "Connect to a Salesforce.com account",
                IconUtils.SALESFORCE_IMAGEPATH, SalesforceDatastore.class, SalesforceDatastoreDialog.class));
        panel.add(createNewDatastoreButton("SugarCRM", "Connect to a SugarCRM system",
                IconUtils.SUGAR_CRM_IMAGEPATH, SugarCrmDatastore.class, SugarCrmDatastoreDialog.class));

        panel.add(Box.createHorizontalStrut(10));

        panel.add(createNewDatastoreButton("MongoDB database", "Connect to a MongoDB database",
                IconUtils.MONGODB_IMAGEPATH, MongoDbDatastore.class, MongoDbDatastoreDialog.class));

        panel.add(createNewDatastoreButton("CouchDB database", "Connect to an Apache CouchDB database",
                IconUtils.COUCHDB_IMAGEPATH, CouchDbDatastore.class, CouchDbDatastoreDialog.class));

        panel.add(createNewDatastoreButton("HBase database", "Connect to an Apache HBase database",
                IconUtils.HBASE_IMAGEPATH, HBaseDatastore.class, HBaseDatastoreDialog.class));

        // set of databases that are displayed directly on panel
        final Set<String> databaseNames = new HashSet<String>();

        createDefaultDatabaseButtons(panel, databaseNames);

        final JButton moreDatastoreTypesButton = new JButton("More",
                imageManager.getImageIcon(IconUtils.FILE_FOLDER, IconUtils.ICON_SIZE_SMALL));
        moreDatastoreTypesButton.setMargin(new Insets(1, 1, 1, 4));
        moreDatastoreTypesButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JPopupMenu popup = new JPopupMenu();

                // installed databases
                final List<DatabaseDriverDescriptor> databaseDrivers = _databaseDriverCatalog
                        .getInstalledWorkingDatabaseDrivers();
                for (DatabaseDriverDescriptor databaseDriver : databaseDrivers) {
                    final String databaseName = databaseDriver.getDisplayName();
                    if (!databaseNames.contains(databaseName)) {
                        final String imagePath = databaseDriver.getIconImagePath();
                        final ImageIcon icon = imageManager.getImageIcon(imagePath, IconUtils.ICON_SIZE_SMALL);
                        final JMenuItem menuItem = WidgetFactory.createMenuItem(databaseName, icon);
                        menuItem.addActionListener(createJdbcActionListener(databaseName));
                        popup.add(menuItem);
                    }
                }

                // custom/other jdbc connection
                {
                    final ImageIcon icon = imageManager.getImageIcon(IconUtils.GENERIC_DATASTORE_IMAGEPATH,
                            IconUtils.ICON_SIZE_SMALL);
                    final JMenuItem menuItem = WidgetFactory.createMenuItem("Other database", icon);
                    menuItem.addActionListener(createJdbcActionListener(null));
                    popup.add(menuItem);
                }

                // composite datastore
                final JMenuItem compositeMenuItem = WidgetFactory.createMenuItem("Composite datastore",
                        imageManager.getImageIcon(IconUtils.COMPOSITE_IMAGEPATH, IconUtils.ICON_SIZE_SMALL));
                compositeMenuItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        new CompositeDatastoreDialog(_datastoreCatalog,
                                _analysisJobBuilderWindow.getWindowContext()).setVisible(true);
                    }
                });

                final JMenuItem databaseDriversMenuItem = WidgetFactory.createMenuItem("Manage database drivers...",
                        imageManager.getImageIcon(IconUtils.MENU_OPTIONS, IconUtils.ICON_SIZE_SMALL));
                databaseDriversMenuItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        OptionsDialog dialog = _optionsDialogProvider.get();
                        dialog.selectDatabaseDriversTab();
                        dialog.setVisible(true);
                    }
                });

                popup.add(databaseDriversMenuItem);
                popup.add(new JSeparator(JSeparator.HORIZONTAL));
                popup.add(compositeMenuItem);
                popup.setBorder(WidgetUtils.BORDER_THIN);

                popup.show(moreDatastoreTypesButton, 0, moreDatastoreTypesButton.getHeight());
            }
        });

        panel.add(Box.createHorizontalStrut(10));
        panel.add(moreDatastoreTypesButton);

        return panel;
    }

    private void createDefaultDatabaseButtons(DCPanel panel, Set<String> databaseNames) {
        if (_databaseDriverCatalog.isInstalled(DatabaseDriverCatalog.DATABASE_NAME_MYSQL)) {
            panel.add(createNewJdbcDatastoreButton("MySQL connection", "Connect to a MySQL database",
                    "images/datastore-types/databases/mysql.png", DatabaseDriverCatalog.DATABASE_NAME_MYSQL,
                    databaseNames));
        }
        if (_databaseDriverCatalog.isInstalled(DatabaseDriverCatalog.DATABASE_NAME_POSTGRESQL)) {
            panel.add(createNewJdbcDatastoreButton("PostgreSQL connection", "Connect to a PostgreSQL database",
                    "images/datastore-types/databases/postgresql.png",
                    DatabaseDriverCatalog.DATABASE_NAME_POSTGRESQL, databaseNames));
        }
        if (_databaseDriverCatalog.isInstalled(DatabaseDriverCatalog.DATABASE_NAME_ORACLE)) {
            panel.add(createNewJdbcDatastoreButton("Oracle connection", "Connect to a Oracle database",
                    "images/datastore-types/databases/oracle.png", DatabaseDriverCatalog.DATABASE_NAME_ORACLE,
                    databaseNames));
        }
        if (_databaseDriverCatalog.isInstalled(DatabaseDriverCatalog.DATABASE_NAME_MICROSOFT_SQL_SERVER_JTDS)) {
            panel.add(createNewJdbcDatastoreButton("Microsoft SQL Server connection",
                    "Connect to a Microsoft SQL Server database", "images/datastore-types/databases/microsoft.png",
                    DatabaseDriverCatalog.DATABASE_NAME_MICROSOFT_SQL_SERVER_JTDS, databaseNames));
        }
    }

    private <D extends Datastore> JButton createNewDatastoreButton(final String title, final String description,
            final String imagePath, final Class<D> datastoreClass,
            final Class<? extends AbstractDialog> dialogClass) {
        final ImageIcon icon = imageManager.getImageIcon(imagePath);
        final JButton button = WidgetFactory.createImageButton(icon);

        final DCPopupBubble popupBubble = new DCPopupBubble(_glassPane,
                "<html><b>" + title + "</b><br/>" + description + "</html>", 0, 0, imagePath);
        popupBubble.attachTo(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                Injector injectorWithNullDatastore = _injectorBuilder.with(datastoreClass, null).createInjector();
                AbstractDialog dialog = injectorWithNullDatastore.getInstance(dialogClass);
                dialog.setVisible(true);
            }
        });
        return button;
    }

    private JButton createNewJdbcDatastoreButton(final String title, final String description,
            final String imagePath, final String databaseName, Set<String> databaseNames) {

        databaseNames.add(databaseName);

        final ImageIcon icon = imageManager.getImageIcon(imagePath);
        final JButton button = WidgetFactory.createImageButton(icon);

        final DCPopupBubble popupBubble = new DCPopupBubble(_glassPane,
                "<html><b>" + title + "</b><br/>" + description + "</html>", 0, 0, imagePath);
        popupBubble.attachTo(button);

        button.addActionListener(createJdbcActionListener(databaseName));

        return button;
    }

    private ActionListener createJdbcActionListener(final String databaseName) {
        return new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                Injector injectorWithDatastore = _injectorBuilder.with(JdbcDatastore.class, null).createInjector();
                JdbcDatastoreDialog dialog = injectorWithDatastore.getInstance(JdbcDatastoreDialog.class);
                dialog.setSelectedDatabase(databaseName);
                dialog.setVisible(true);
            }
        };
    }

    @Override
    public void addNotify() {
        super.addNotify();
        _datastoreCatalog.addListener(this);
    }

    @Override
    public void removeNotify() {
        super.removeNotify();
        _datastoreCatalog.removeListener(this);
    }

    @Override
    public void onAdd(Datastore datastore) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                updateDatastores();
            }
        });
    }

    @Override
    public void onRemove(Datastore datastore) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                updateDatastores();
            }
        });
    }

    private void selectFirstVisibleDatastore() {
        boolean found = false;

        for (DatastorePanel datastorePanel : _datastorePanels) {
            if (datastorePanel.isVisible()) {
                setSelectedDatastorePanel(datastorePanel);
                found = true;
                break;
            }
        }

        _analyzeButton.setEnabled(found);
    }

    private void selectNextVisibleDatastore() {
        DatastorePanel selectedDatastorePanel = getSelectedDatastorePanel();
        if (selectedDatastorePanel != null) {

            int indexOf = _datastorePanels.indexOf(selectedDatastorePanel);
            for (int i = indexOf + 1; i < _datastorePanels.size(); i++) {
                DatastorePanel panel = _datastorePanels.get(i);
                if (panel.isVisible()) {
                    setSelectedDatastorePanel(panel);
                    break;
                }
            }
        }
    }

    private void selectPreviousVisibleDatastore() {
        DatastorePanel selectedDatastorePanel = getSelectedDatastorePanel();
        if (selectedDatastorePanel != null) {

            int indexOf = _datastorePanels.indexOf(selectedDatastorePanel);
            for (int i = indexOf - 1; i >= 0; i--) {
                DatastorePanel panel = _datastorePanels.get(i);
                if (panel.isVisible()) {
                    setSelectedDatastorePanel(panel);
                    break;
                }
            }
        }
    }

    public void setSelectedDatastorePanel(DatastorePanel datastorePanel) {
        for (DatastorePanel panel : _datastorePanels) {
            if (datastorePanel == panel) {
                panel.setSelected(true);
            } else {
                panel.setSelected(false);
            }
        }
        requestSearchFieldFocus();
    }

    public DatastorePanel getSelectedDatastorePanel() {
        for (DatastorePanel panel : _datastorePanels) {
            if (panel.isVisible() && panel.isSelected()) {
                return panel;
            }
        }
        return null;
    }

    public void clickAnalyzeButton() {
        if (_analyzeButton.isEnabled()) {
            _analyzeButton.doClick();
        }
    }

    public void requestSearchFieldFocus() {
        _searchDatastoreTextField.requestFocus();
    }
}