/**
* 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.datacleaner.widgets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import org.apache.metamodel.MetaModelHelper;
import org.apache.metamodel.schema.Column;
import org.apache.metamodel.schema.Schema;
import org.apache.metamodel.schema.Table;
import org.datacleaner.connection.Datastore;
import org.datacleaner.connection.DatastoreConnection;
import org.datacleaner.util.SchemaComparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A combobox that makes it easy to display and select source coumns from a
* list. The list can either be populated based on a datastore (in which case
* the list will include all schemas, all tables and all columns) as well as
* just a single table (in which case it will only include columns from that
* table).
*/
public class SourceColumnComboBox extends DCComboBox<Object> {
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(SourceColumnComboBox.class);
private final SchemaStructureComboBoxListRenderer _renderer;
private volatile DatastoreConnection _datastoreConnection;
private volatile Table _table;
public SourceColumnComboBox() {
super();
_renderer = new SchemaStructureComboBoxListRenderer();
setRenderer(_renderer);
setEditable(false);
}
public SourceColumnComboBox(final Datastore datastore) {
this();
setModel(datastore);
}
public SourceColumnComboBox(final Datastore datastore, final Table table) {
this();
setModel(datastore, table);
}
public void setEmptyModel() {
setModel(null, null);
}
public void setModel(final Datastore datastore, final Table table) {
final String previousColumnName;
final Column previousItem = getSelectedItem();
if (previousItem == null) {
previousColumnName = null;
} else {
previousColumnName = previousItem.getName();
}
if (getTable() == table) {
return;
}
setTable(table);
if (datastore == null) {
setDatastoreConnection(null);
} else {
setDatastoreConnection(datastore.openConnection());
}
if (table == null) {
setModel(new DefaultComboBoxModel<>(new String[1]));
} else {
int selectedIndex = 0;
final List<Column> comboBoxList = new ArrayList<>();
comboBoxList.add(null);
final Column[] columns = table.getColumns();
for (final Column column : columns) {
comboBoxList.add(column);
if (column.getName().equals(previousColumnName)) {
selectedIndex = comboBoxList.size() - 1;
}
}
final ComboBoxModel<Object> model = new DefaultComboBoxModel<>(comboBoxList.toArray());
setModel(model);
setSelectedIndex(selectedIndex);
}
}
public void setModel(final Datastore datastore) {
setModel(datastore, true);
}
public void setModel(final Table table) {
setModel(null, table);
}
public void setModel(final Datastore datastore, final boolean retainSelection) {
final Column previousItem = getSelectedItem();
setTable(null);
if (datastore == null) {
setDatastoreConnection(null);
setModel(new DefaultComboBoxModel<>(new String[1]));
} else {
final DatastoreConnection con = setDatastoreConnection(datastore.openConnection());
int selectedIndex = 0;
final List<Object> comboBoxList = new ArrayList<>();
comboBoxList.add(null);
final Schema[] schemas = con.getSchemaNavigator().getSchemas();
Arrays.sort(schemas, new SchemaComparator());
for (final Schema schema : schemas) {
comboBoxList.add(schema);
if (!MetaModelHelper.isInformationSchema(schema)) {
final Table[] tables = schema.getTables();
for (final Table table : tables) {
try {
final Column[] columns = table.getColumns();
if (columns != null && columns.length > 0) {
comboBoxList.add(table);
for (final Column column : columns) {
comboBoxList.add(column);
if (column == previousItem) {
selectedIndex = comboBoxList.size() - 1;
}
}
}
} catch (final Exception e) {
// errors can occur for experimental datastores (or
// something like SAS datastores where not all SAS
// files are supported). Ignore.
logger.error("Error occurred getting columns of table: {}", table);
}
}
}
}
final ComboBoxModel<Object> model = new DefaultComboBoxModel<>(comboBoxList.toArray());
setModel(model);
if (retainSelection) {
setSelectedIndex(selectedIndex);
}
}
}
private void setIndentation() {
_renderer.setIndentEnabled(_table == null && _datastoreConnection != null);
}
public Table getTable() {
return _table;
}
private void setTable(final Table table) {
_table = table;
setIndentation();
}
public void addColumnSelectedListener(final DCComboBox.Listener<Column> listener) {
super.addListener(item -> {
if (item instanceof Column) {
listener.onItemSelected((Column) item);
}
});
}
private DatastoreConnection setDatastoreConnection(final DatastoreConnection datastoreConnection) {
if (_datastoreConnection != null) {
// close the previous data context provider
_datastoreConnection.close();
}
_datastoreConnection = datastoreConnection;
setIndentation();
return _datastoreConnection;
}
@Override
public Column getSelectedItem() {
final Object selectedItem = super.getSelectedItem();
if (selectedItem instanceof Column) {
return (Column) selectedItem;
}
return null;
}
@Override
public void setSelectedItem(Object value) {
if (value instanceof String) {
if (_table == null) {
// cannot string convert to column without a table.
value = null;
} else {
value = _table.getColumnByName((String) value);
}
}
super.setSelectedItem(value);
}
@Override
public void removeNotify() {
super.removeNotify();
if (_datastoreConnection != null) {
// close the data context provider when the widget is removed
_datastoreConnection.close();
}
}
}