/* Copyright (C) 2003 EBI, GRL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.ensembl.mart.explorer; import java.awt.Component; import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileFilter; import org.ensembl.mart.guiutils.DatabaseSettingsDialog; import org.ensembl.mart.lib.DetailedDataSource; import org.ensembl.mart.lib.config.AttributeDescription; import org.ensembl.mart.lib.config.ConfigurationException; import org.ensembl.mart.lib.config.DSConfigAdaptor; import org.ensembl.mart.lib.config.DatabaseDSConfigAdaptor; import org.ensembl.mart.lib.config.DatasetConfig; import org.ensembl.mart.lib.config.RegistryDSConfigAdaptor; import org.ensembl.mart.lib.config.URLDSConfigAdaptor; import org.ensembl.mart.util.LoggingUtil; import com.mysql.jdbc.Connection; /** * Widget representing availabled adaptors and enabling user to add, and delete * them. * */ public class AdaptorManager extends Box { private boolean advancedOptionsEnabled; private static final Logger logger = Logger.getLogger(AdaptorManager.class.getName()); private Feedback feedback = new Feedback(this); private static final String DS_CONFIG_FILE_KEY = "CONFIG_FILE_KEY"; private static final String REGISTRY_FILE_KEY = "REGISTRY_FILE_KEY"; private static final String OPTIONAL_ENABLED_KEY = "OPTIONAL_ENABLED"; private String none = "None"; private RegistryDSConfigAdaptor rootAdaptor = new RegistryDSConfigAdaptor(false, true); //dont ignore cache, and dont include hidden members (these are only for MartEditor) private Map optionToConfig = new HashMap(); /** Persistent preferences object used to hold user history. */ private Preferences prefs = Preferences.userNodeForPackage(this.getClass()); private LabelledComboBox combo = new LabelledComboBox("Adaptor"); private JFileChooser dsConfigFileChooser = new JFileChooser(); private JFileChooser registryFileChooser = new JFileChooser(); private FileFilter xmlFilter = new FileFilter() { public boolean accept(File f) { return f != null && (f.isDirectory() || f.getName().toLowerCase().endsWith(".xml")); } public String getDescription() { return "XML Files"; } }; private DatabaseSettingsDialog databaseDialog = new DatabaseSettingsDialog(); /** * This widget is part of a system based on the MVC design pattern. From this * perspective the widget is a Config and a Controller and the query is the * Model. * * @param loadPreferences whether or not to load cached preferences * underlying model for this widget. */ public AdaptorManager(boolean loadPreferences) { super(BoxLayout.Y_AXIS); databaseDialog.addDatabaseType("mysql"); databaseDialog.addDriver("com.mysql.jdbc.Driver"); databaseDialog.setPrefs(Preferences.userNodeForPackage(this.getClass())); if (loadPreferences) loadPrefs(); createUI(); } public AdaptorManager() { this(true); } private void createUI() { combo.setEditable(false); //combo.setSelectedItem(none); initFileChoosers(); JButton importRegistry = new JButton("Add Registry File"); importRegistry.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doImportRegistry(); } }); JButton addFile = new JButton("Add Dataset Configuration File"); addFile.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doAddFile(); } }); JButton delete = new JButton("Remove"); delete.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doDelete(); } }); JButton deleteAll = new JButton("Remove All"); deleteAll.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doDeleteAll(); } }); Box top = Box.createHorizontalBox(); top.add(combo); Box bottom = Box.createHorizontalBox(); bottom.add(Box.createHorizontalGlue()); bottom.add(importRegistry); bottom.add(addFile); bottom.add(delete); bottom.add(deleteAll); add(top); add(bottom); } /** * Presents the user with a file chooser dialog with which she can choose a * registry file to import. */ protected void doImportRegistry() { // user chooses file int action = registryFileChooser.showOpenDialog(this); // convert file contents into string if (action == JFileChooser.APPROVE_OPTION) { File f = registryFileChooser.getSelectedFile().getAbsoluteFile(); prefs.put(REGISTRY_FILE_KEY, f.toString()); try { importRegistry(f.toURL()); } catch (MalformedURLException e) { feedback.warning(e); } } } /** * @param url */ public void importRegistry(URL url) { try { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); RegistryDSConfigAdaptor ra = new RegistryDSConfigAdaptor(url, false, false, true); //see notes for rootAdaptor DSConfigAdaptor[] as = ra.getLeafAdaptors(); for (int i = 0; i < as.length; i++) { add(as[i]); } } catch (ConfigurationException e) { JOptionPane.showMessageDialog(this, "Problem loading the url: " + url + ": " + e.getMessage()); } finally { setCursor(Cursor.getDefaultCursor()); } } /** * Removes all adaptors. * */ private void doDeleteAll() { DSConfigAdaptor as[]; try { as = rootAdaptor.getLeafAdaptors(); } catch (ConfigurationException e1) { throw new RuntimeException( "Recieved ConfigurationException getting adaptors from root Adaptor: " + e1.getMessage() + "\n", e1); } for (int i = 0; i < as.length; i++) rootAdaptor.remove(as[i]); try { updateWidget(rootAdaptor.getLeafAdaptors()); storePrefs(); } catch (ConfigurationException e) { feedback.warning(e); } } /** * Updates state of widget after a user action. * * @param configs */ private void updateWidget(DSConfigAdaptor[] adaptors) throws ConfigurationException { optionToConfig.clear(); // map string -> adaptor for (int i = 0; i < adaptors.length; i++) { DSConfigAdaptor a = adaptors[i]; optionToConfig.put(a.getName(), a); logger.fine("Added" + a.getName() + ":" + a); } // sort List l = new ArrayList(optionToConfig.keySet()); Collections.sort(l); combo.removeAllItems(); combo.addAll(l); } /** * */ protected void doAddDatabase() { boolean valid = false; DetailedDataSource ds = null; while (!valid) { if (!databaseDialog.showDialog(this,"")) break; String defaultSourceName = databaseDialog.getConnectionName(); if (defaultSourceName == null || defaultSourceName.length() < 1) defaultSourceName = DetailedDataSource.defaultName( databaseDialog.getHost(), databaseDialog.getPort(), databaseDialog.getDatabase(), databaseDialog.getSchema(), databaseDialog.getUser()); ds = new DetailedDataSource( databaseDialog.getDatabaseType(), databaseDialog.getHost(), databaseDialog.getPort(), databaseDialog.getDatabase(), databaseDialog.getSchema(), databaseDialog.getUser(), databaseDialog.getPassword(), 10, databaseDialog.getDriver(), defaultSourceName); Connection conn = null; try { ds.getConnection(); valid = true; } catch (SQLException e) { //warning dialog then retry feedback.warning("Could not connect to Database\nwith the given Connection Settings.\nPlease try again!"); valid = false; } finally { DetailedDataSource.close(conn); } } if (valid) { try { DSConfigAdaptor a = new DatabaseDSConfigAdaptor(ds, databaseDialog.getUser(), databaseDialog.getMartUser(), false, false, true, true); //see notes for rootAdaptor for boolean settings // TODO bind a and ds so that can recreate the link after persistence add(a); } catch (ConfigurationException e1) { feedback.warning( "Couldn not load DatasetConfigs from \"" + ds + "\". It might be possible to execute queries against this database.", e1, false); } } } private void loadPrefs() { advancedOptionsEnabled = prefs.getBoolean(OPTIONAL_ENABLED_KEY, false); } /** * Delete selected datasetconfig. */ public void doDelete() { if (combo.getSelectedItem() == null) return; try { Object selected = combo.getSelectedItem(); int newIndex = -1; int index = combo.indexOfItem(selected); int max = combo.getItemCount(); if (index + 1 < max) newIndex = index + 1; else if (index - 1 <= max) newIndex = index - 1; // remove the selected item rootAdaptor.remove((DSConfigAdaptor) optionToConfig.get(selected)); updateWidget(rootAdaptor.getLeafAdaptors()); // select the "next" item if (newIndex > -1) combo.setSelectedItem(combo.getItemAt(newIndex)); storePrefs(); } catch (ConfigurationException e) { feedback.warning("Failed to delete adaptor " + combo.getSelectedItem(), e); } } /** * Presents the user with a file chooser dialog with which she can choose a * configuration file. */ protected void doAddFile() { // user chooses file int action = dsConfigFileChooser.showOpenDialog(this); // convert file contents into string if (action == JFileChooser.APPROVE_OPTION) { File f = dsConfigFileChooser.getSelectedFile().getAbsoluteFile(); prefs.put(DS_CONFIG_FILE_KEY, f.toString()); try { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); URLDSConfigAdaptor adaptor = new URLDSConfigAdaptor(f.toURL(), false, false); // see notes for rootAdaptor for boolean settings // TODO resolve any name clashes, i.e. existing dsv with same name // this.adaptor.add(adaptor); add(adaptor); combo.setSelectedItem(adaptor.getName()); } catch (MalformedURLException e) { JOptionPane.showMessageDialog(this, "File " + f.toString() + " not found: " + e.getMessage()); } catch (ConfigurationException e) { JOptionPane.showMessageDialog( this, "Problem loading the Failed to load file: " + f.toString() + ": " + e.getMessage()); } finally { setCursor(Cursor.getDefaultCursor()); } } } /** * Runs a test; an instance of this class is shown in a Frame. */ public static void main(String[] args) throws Exception { LoggingUtil.setAllRootHandlerLevelsToFinest(); logger.setLevel(Level.FINE); AdaptorManager dvm = new AdaptorManager(); dvm.setSize(950, 750); dvm.showDialog(null); System.exit(0); } /** * Convenience method which opens this instance in a dialog box. * * @param parent * parent for the dialog box. */ public void showDialog(Component parent) { int option = JOptionPane.showOptionDialog( parent, this, "Adaptors", JOptionPane.OK_CANCEL_OPTION, JOptionPane.DEFAULT_OPTION, null, null, null); } private void initFileChoosers() { String lastChosenFile = prefs.get(DS_CONFIG_FILE_KEY, null); if (lastChosenFile != null) { dsConfigFileChooser.setSelectedFile(new File(lastChosenFile)); } dsConfigFileChooser.addChoosableFileFilter(xmlFilter); lastChosenFile = prefs.get(REGISTRY_FILE_KEY, null); if (lastChosenFile != null) { registryFileChooser.setSelectedFile(new File(lastChosenFile)); } registryFileChooser.addChoosableFileFilter(xmlFilter); } public boolean contains(DatasetConfig dsv) { try { return dsv != null && rootAdaptor.containsDatasetConfig(dsv); } catch (ConfigurationException e) { // Shouldn't happen feedback.warning(e); } return false; } public void add(DSConfigAdaptor a) throws ConfigurationException { rootAdaptor.add(a); updateWidget(rootAdaptor.getLeafAdaptors()); storePrefs(); } private void storePrefs() throws ConfigurationException { prefs.putBoolean(OPTIONAL_ENABLED_KEY, advancedOptionsEnabled); try { prefs.flush(); } catch (BackingStoreException e) { feedback.warning(e); } } public RegistryDSConfigAdaptor getRootAdaptor() { return rootAdaptor; } public void setAdvancedOptionsEnabled(boolean b) { this.advancedOptionsEnabled = b; // just store this pref, rather than using storePrefs(), because that // method will // be expensive if many adaptors are loaded. prefs.putBoolean(OPTIONAL_ENABLED_KEY, advancedOptionsEnabled); try { prefs.flush(); } catch (BackingStoreException e) { feedback.warning(e); } } public boolean isAdvancedOptionsEnabled() { return advancedOptionsEnabled; } /** * Resets the state of the manager. Removes all adaptors and sets * advancedOptionsEnabled=false; */ public void reset() { rootAdaptor = new RegistryDSConfigAdaptor(false, false); try { updateWidget(rootAdaptor.getLeafAdaptors()); storePrefs(); } catch (ConfigurationException e) { feedback.warning(e); } setAdvancedOptionsEnabled(false); } public void clearCache() { rootAdaptor.clearCache(); } public AttributeDescription getPointerAttribute(AttributeDescription pointer) { DatasetConfig d = null; try { d = getRootAdaptor().getDatasetConfigByDatasetInternalName(pointer.getPointerDataset(), "default"); } catch (ConfigurationException e) { throw new RuntimeException("Could not get pointer dataset for " + pointer.getInternalName() + "\n"); } if (d == null) throw new RuntimeException("Could not get pointer dataset for " + pointer.getInternalName() + "\n"); AttributeDescription ret = null; ret = d.getAttributeDescriptionByInternalName(pointer.getPointerAttribute()); if ( ret == null ) throw new RuntimeException("Could not get pointer attribute for " + pointer.getInternalName() + "\n"); return ret; } }