/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.properties.celleditors.value; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.io.Serializable; import java.sql.SQLException; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import javax.swing.AbstractCellEditor; import javax.swing.AbstractListModel; import javax.swing.ComboBoxModel; import javax.swing.JComboBox; import javax.swing.JTable; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import com.rapidminer.gui.tools.ProgressThread; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.operator.Operator; import com.rapidminer.parameter.ParameterType; import com.rapidminer.parameter.ParameterTypeDatabaseSchema; import com.rapidminer.parameter.ParameterTypeDatabaseTable; import com.rapidminer.parameter.UndefinedParameterError; import com.rapidminer.tools.LogService; import com.rapidminer.tools.Tools; import com.rapidminer.tools.jdbc.ColumnIdentifier; import com.rapidminer.tools.jdbc.DatabaseHandler; import com.rapidminer.tools.jdbc.TableName; import com.rapidminer.tools.jdbc.connection.ConnectionEntry; import com.rapidminer.tools.jdbc.connection.ConnectionProvider; /** Displays a combo box with table names. Can work in two operation {@link Mode}s, one displaying * table names, and one displaying schema names. * * * @author Tobias Malbrecht */ public class DatabaseTableValueCellEditor extends AbstractCellEditor implements PropertyValueCellEditor { private static final long serialVersionUID = -771727412083431607L; private enum Mode { TABLE, SCHEMA }; private Mode mode; class DatabaseTableComboBoxModel extends AbstractListModel implements ComboBoxModel, Serializable { private static final long serialVersionUID = -2984664300141879731L; private String lastURL = null; private String lastSelectedSchema = null; private LinkedList<Object> list = new LinkedList<Object>(); private Object selected = null; public boolean updateModel() { final Object selected = getValue(); boolean schemaChanged = false; if (mode == Mode.TABLE) { String selectedSchema = null; if (DatabaseTableValueCellEditor.this.operator != null) { if (!DatabaseTableValueCellEditor.this.operator.getParameterAsBoolean(DatabaseHandler.PARAMETER_USE_DEFAULT_SCHEMA)) { try { selectedSchema = DatabaseTableValueCellEditor.this.operator.getParameterAsString(DatabaseHandler.PARAMETER_SCHEMA_NAME); if ((selectedSchema != null) && selectedSchema.isEmpty()) { selectedSchema = null; } } catch (UndefinedParameterError e) { selectedSchema = null; } } } schemaChanged = !Tools.equals(selectedSchema, lastSelectedSchema); lastSelectedSchema = selectedSchema; } if (connectionProvider != null) { final ConnectionEntry entry = connectionProvider.getConnectionEntry(); if (entry != null && (schemaChanged || (lastURL == null) || !lastURL.equals(entry.getURL()))) { lastURL = entry.getURL(); ProgressThread t = new ProgressThread("fetching_database_tables") { @Override public void run() { getProgressListener().setTotal(100); getProgressListener().setCompleted(10); try { list.clear(); DatabaseHandler handler = null; try { handler = DatabaseHandler.getConnectedDatabaseHandler(entry); } catch (SQLException e1) { LogService.getRoot().log(Level.WARNING, "Failed to fetch database tables: "+e1, e1); SwingTools.showSimpleErrorMessage("failed_to_fetch_database_tables", e1, entry.getName(), e1.getMessage()); return; } getProgressListener().setCompleted(20); if (handler != null) { Map<TableName, List<ColumnIdentifier>> tableMap; try { tableMap = handler.getAllTableMetaData(getProgressListener(), 20, 90, false); for (TableName tn : tableMap.keySet()) { switch (DatabaseTableValueCellEditor.this.mode) { case TABLE: if (lastSelectedSchema != null) { if (!lastSelectedSchema.equals(tn.getSchema())) { continue; } } list.add(tn.getTableName()); break; case SCHEMA: if (!list.contains(tn.getSchema())) { list.add(tn.getSchema()); } break; default: throw new RuntimeException("Illegal mode: "+DatabaseTableValueCellEditor.this.mode); } } //list.addAll(tableMap.keySet()); getProgressListener().setCompleted(90); } catch (SQLException e) { LogService.getRoot().log(Level.WARNING, "Failed to fetch database tables: "+e, e); SwingTools.showSimpleErrorMessage("failed_to_fetch_database_tables", e, entry.getName(), e.getMessage()); return; } try { handler.disconnect(); } catch (SQLException e) { } } if (getSelectedItem() == null) { if (model.getSize() == 0) { setSelectedItem(null); } else { if (selected != null) { setSelectedItem(selected); } else { if (model.getSize() > 0) { setSelectedItem(model.getElementAt(0)); } } } } fireContentsChanged(this, 0, list.size() - 1); } finally { getProgressListener().complete(); } } }; t.start(); return true; } } return false; } @Override public Object getSelectedItem() { return selected; } @Override public void setSelectedItem(Object object) { if ((selected != null && !selected.equals(object)) || selected == null && object != null) { selected = object; fireContentsChanged(this, -1, -1); } } @Override public Object getElementAt(int index) { if (index >= 0 && index < list.size()) { return list.get(index); } return null; } @Override public int getSize() { return list.size(); } } class DatabaseTableComboBox extends JComboBox { private static final long serialVersionUID = 7641636749562465262L; private DatabaseTableComboBox() { super(model); setEditable(true); addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { fireEditingStopped(); } }); addFocusListener(new FocusListener() { @Override public void focusLost(FocusEvent e) { fireEditingStopped(); } @Override public void focusGained(FocusEvent e) { model.updateModel(); } }); addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuCanceled(PopupMenuEvent e) {} @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {} @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { if (model.updateModel()) { hidePopup(); showPopup(); } } }); } } private DatabaseTableComboBoxModel model = new DatabaseTableComboBoxModel(); private JComboBox comboBox = new DatabaseTableComboBox(); private Operator operator; private ParameterType type; private ConnectionProvider connectionProvider; public DatabaseTableValueCellEditor(final ParameterTypeDatabaseSchema type) { this.type = type; this.mode = Mode.SCHEMA; comboBox.setToolTipText(type.getDescription()); } public DatabaseTableValueCellEditor(final ParameterTypeDatabaseTable type) { this.type = type; this.mode = Mode.TABLE; comboBox.setToolTipText(type.getDescription()); } private String getValue() { String value = null; value = operator.getParameters().getParameterOrNull(type.getKey()); return value; } @Override public boolean rendersLabel() { return false; } @Override public boolean useEditorAsRenderer() { return true; } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { model.updateModel(); comboBox.setSelectedItem(value); return comboBox; } @Override public Object getCellEditorValue() { return comboBox.getSelectedItem(); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { model.updateModel(); comboBox.setSelectedItem(value); return comboBox; } @Override public void setOperator(Operator operator) { this.operator = operator; if (operator != null && operator instanceof ConnectionProvider) { this.connectionProvider = (ConnectionProvider) operator; } } }