/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.mappingsplugin.ui.db;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.lang.reflect.InvocationTargetException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonModel;
import javax.swing.ComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.text.Document;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext;
import org.eclipse.persistence.tools.workbench.framework.ui.dialog.AbstractDialog;
import org.eclipse.persistence.tools.workbench.framework.ui.dialog.WaitDialog;
import org.eclipse.persistence.tools.workbench.framework.uitools.CursorConstants;
import org.eclipse.persistence.tools.workbench.framework.uitools.DualListSelectorPanel;
import org.eclipse.persistence.tools.workbench.framework.uitools.SwingComponentFactory;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWDatabase;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.db.ExternalTableDescription;
import org.eclipse.persistence.tools.workbench.uitools.app.CollectionValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.ListValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.PropertyValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.ReadOnlyListValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.SimpleCollectionValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.SimplePropertyValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.SortedListValueModelAdapter;
import org.eclipse.persistence.tools.workbench.uitools.app.swing.CheckBoxModelAdapter;
import org.eclipse.persistence.tools.workbench.uitools.app.swing.ComboBoxModelAdapter;
import org.eclipse.persistence.tools.workbench.uitools.app.swing.DocumentAdapter;
import org.eclipse.persistence.tools.workbench.uitools.cell.SimpleListCellRenderer;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
/**
* This dialog has two functions:
* First, it allows the user to query the database for tables that can
* be imported. The user can specify various search criteria (catalog,
* schema pattern, table name pattern, table type) to narrow the
* list of tables.
* Second, the user can select from this list of tables the tables to
* be imported (or refreshed, if they are already present in the project).
*
* It might take a while for this dialog to appear since it will query the
* database for catalogs, schemas, and table types to populate the
* combo-boxes in the search criteria panel.
*/
final class TableImporterDialog
extends AbstractDialog
{
MWDatabase database;
private static final String DEFAULT_TABLE_NAME_PATTERN = "%";
private PropertyValueModel tableNamePatternHolder;
private Document tableNamePatternDocument;
// initial focus component
private JTextField tableNamePatternTextField;
private JButton tablesButton;
private String noCatalog;
private String ignoreCatalog;
private PropertyValueModel catalogHolder;
private ComboBoxModel catalogComboBoxModel;
private String noSchema;
private String ignoreSchema;
private PropertyValueModel schemaPatternHolder;
private ComboBoxModel schemaPatternComboBoxModel;
private static final String DEFAULT_TABLE_TYPE = "TABLE";
private String allTableTypes;
private PropertyValueModel tableTypeHolder;
private ComboBoxModel tableTypeComboBoxModel;
/**
* if the "fully-qualified" check-box is checked, the tables will
* be imported with their fully-qualified names; if it is not checked
* the tables will be imported with only their "short" names;
* the default is to use only the "short" names
*/
private PropertyValueModel fullyQualifiedHolder;
private ButtonModel fullyQualifiedCheckBoxModel;
SimpleCollectionValueModel availableExternalTableDescriptionsHolder;
SimpleCollectionValueModel selectedExternalTableDescriptionsHolder;
private static final Comparator EXTERNAL_TABLE_DESCRIPTION_COMPARATOR =
new Comparator() {
public int compare(Object o1, Object o2) {
return Collator.getInstance().compare(((ExternalTableDescription) o1).getQualifiedName(), ((ExternalTableDescription) o2).getQualifiedName());
}
};
TableImporterDialog(WorkbenchContext context, MWDatabase database) {
super(context);
this.initialize(database);
}
protected void initialize() {
super.initialize();
this.setTitle(this.resourceRepository().getString("IMPORT_TABLES_FROM_DATABASE_DIALOG.title"));
}
protected void prepareToShow() {
this.setSize(700, 500);
this.setLocationRelativeTo(this.getParent());
getRootPane().setDefaultButton(this.tablesButton);
}
private void initialize(MWDatabase db) {
if ( ! db.isConnected()) {
throw new IllegalStateException("database not connected");
}
this.database = db;
// table name pattern
this.tableNamePatternHolder = new SimplePropertyValueModel(DEFAULT_TABLE_NAME_PATTERN);
this.tableNamePatternDocument = new DocumentAdapter(this.tableNamePatternHolder);
this.tableNamePatternTextField = new JTextField(this.tableNamePatternDocument, null, 0);
// catalog
this.ignoreCatalog = this.resourceRepository().getString("IGNORE");
this.noCatalog = this.resourceRepository().getString("NO_CATALOG");
this.catalogHolder = new SimplePropertyValueModel(this.ignoreCatalog);
this.catalogComboBoxModel = new ComboBoxModelAdapter(new ReadOnlyListValueModel(this.buildCatalogs()), this.catalogHolder);
// schema pattern
this.ignoreSchema = this.resourceRepository().getString("IGNORE");
this.noSchema = this.resourceRepository().getString("NO_SCHEMA");
this.schemaPatternHolder = new SimplePropertyValueModel(this.ignoreSchema);
this.schemaPatternComboBoxModel = new ComboBoxModelAdapter(new ReadOnlyListValueModel(this.buildSchemas()), this.schemaPatternHolder);
// table type
this.allTableTypes = this.resourceRepository().getString("ALL_TYPES");
List tableTypes = this.buildTableTypes();
// some platforms do not return any table types, but most will return "TABLE"
this.tableTypeHolder = new SimplePropertyValueModel(
tableTypes.contains(DEFAULT_TABLE_TYPE) ? DEFAULT_TABLE_TYPE : this.allTableTypes
);
this.tableTypeComboBoxModel = new ComboBoxModelAdapter(new ReadOnlyListValueModel(tableTypes), this.tableTypeHolder);
// fully-qualified
this.fullyQualifiedHolder = new SimplePropertyValueModel(Boolean.FALSE);
this.fullyQualifiedCheckBoxModel = new CheckBoxModelAdapter(this.fullyQualifiedHolder);
this.availableExternalTableDescriptionsHolder = new SimpleCollectionValueModel();
this.selectedExternalTableDescriptionsHolder = new SimpleCollectionValueModel();
}
private List buildCatalogs() {
List catalogs = new ArrayList();
catalogs.add(this.ignoreCatalog);
catalogs.add(this.noCatalog);
catalogs.addAll(CollectionTools.sortedSet(this.database.catalogNames()));
return catalogs;
}
private List buildSchemas() {
List schemas = new ArrayList();
schemas.add(this.ignoreSchema);
schemas.add(this.noSchema);
schemas.addAll(CollectionTools.sortedSet(this.database.schemaNames()));
return schemas;
}
private List buildTableTypes() {
List schemas = new ArrayList();
schemas.add(this.allTableTypes);
schemas.addAll(CollectionTools.sortedSet(this.database.tableTypeNames()));
return schemas;
}
protected String helpTopicId() {
return "dialog.importingTables";
}
protected Component initialFocusComponent() {
return this.tableNamePatternTextField;
}
protected Component buildMainPanel() {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
// search criteria
JPanel searchCriteriaPanel = this.buildSearchCriteriaPanel();
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.CENTER;
constraints.insets = new Insets(5, 5, 0, 5);
panel.add(searchCriteriaPanel, constraints);
// dual list selection panel
JPanel tableSelectionPanel = this.buildTableSelectionPanel();
constraints.gridx = 0;
constraints.gridy = 1;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 1;
constraints.fill = GridBagConstraints.BOTH;
constraints.anchor = GridBagConstraints.CENTER;
constraints.insets = new Insets(5, 5, 0, 5);
panel.add(tableSelectionPanel, constraints);
// import tables fully-qualified
JCheckBox fullyQualifiedCheckBox = this.buildFullyQualifiedCheckBox();
this.helpManager().addTopicID(fullyQualifiedCheckBox, this.helpTopicId() + ".fullyQualified");
constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.EAST;
constraints.insets = new Insets(10, 0, 5, 0);
panel.add(fullyQualifiedCheckBox, constraints);
return panel;
}
private JPanel buildSearchCriteriaPanel() {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
// table name pattern text field
JLabel tableLabel = new JLabel(this.resourceRepository().getString("TABLE_NAME_PATTERN_TEXT_FIELD_LABEL"));
tableLabel.setDisplayedMnemonic(this.resourceRepository().getMnemonic("TABLE_NAME_PATTERN_TEXT_FIELD_LABEL"));
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(0, 0, 0, 0);
panel.add(tableLabel, constraints);
// the table name patter text field is pre-built
this.helpManager().addTopicID(this.tableNamePatternTextField, this.helpTopicId() + ".namePattern");
constraints.gridx = 1;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(0, 5, 0, 0);
panel.add(this.tableNamePatternTextField, constraints);
tableLabel.setLabelFor(this.tableNamePatternTextField);
// catalog combo-box (NOT editable)
JLabel catalogLabel = new JLabel(this.resourceRepository().getString("CATALOG_COMBO_BOX_LABEL"));
catalogLabel.setDisplayedMnemonic(this.resourceRepository().getMnemonic("CATALOG_COMBO_BOX_LABEL"));
constraints.gridx = 0;
constraints.gridy = 1;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(5, 0, 0, 0);
panel.add(catalogLabel, constraints);
JComboBox catalogComboBox = new JComboBox(this.catalogComboBoxModel);
this.helpManager().addTopicID(catalogComboBox, this.helpTopicId() + ".catalog");
constraints.gridx = 1;
constraints.gridy = 1;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(5, 5, 0, 0);
panel.add(catalogComboBox, constraints);
catalogLabel.setLabelFor(catalogComboBox);
// schema combo-box (editable)
JLabel schemaLabel = new JLabel(this.resourceRepository().getString("SCHEMA_PATTERN_COMBO_BOX_LABEL"));
schemaLabel.setDisplayedMnemonic(this.resourceRepository().getMnemonic("SCHEMA_PATTERN_COMBO_BOX_LABEL"));
constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(5, 0, 0, 0);
panel.add(schemaLabel, constraints);
JComboBox schemaComboBox = new JComboBox(this.schemaPatternComboBoxModel);
schemaComboBox.setEditable(true);
this.helpManager().addTopicID(schemaComboBox, this.helpTopicId() + ".schemaPattern");
constraints.gridx = 1;
constraints.gridy = 2;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(5, 5, 0, 0);
panel.add(schemaComboBox, constraints);
schemaLabel.setLabelFor(schemaComboBox);
// table type combo-box
JLabel tableTypeLabel = new JLabel(this.resourceRepository().getString("TABLE_TYPE_COMBO_BOX_LABEL"));
tableTypeLabel.setDisplayedMnemonic(this.resourceRepository().getMnemonic("TABLE_TYPE_COMBO_BOX_LABEL"));
constraints.gridx = 0;
constraints.gridy = 3;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(5, 0, 0, 0);
panel.add(tableTypeLabel, constraints);
JComboBox tableTypeComboBox = new JComboBox(this.tableTypeComboBoxModel);
this.helpManager().addTopicID(tableTypeComboBox, helpTopicId() + ".type");
constraints.gridx = 1;
constraints.gridy = 3;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(5, 5, 0, 0);
panel.add(tableTypeComboBox, constraints);
tableTypeLabel.setLabelFor(tableTypeComboBox);
this.tablesButton = new JButton(this.buildGetTableNamesAction());
this.tablesButton.setMnemonic(this.resourceRepository().getMnemonic("GET_TABLE_NAMES_BUTTON_TEXT"));
constraints.gridx = 0;
constraints.gridy = 4;
constraints.gridwidth = 2;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(10, 0, 5, 0);
panel.add(this.tablesButton, constraints);
return panel;
}
private Action buildGetTableNamesAction() {
return new AbstractAction(this.resourceRepository().getString("GET_TABLE_NAMES_BUTTON_TEXT")) {
public void actionPerformed(ActionEvent e) {
TableImporterDialog.this.getTableNamesPressed();
}
};
}
private JCheckBox buildFullyQualifiedCheckBox() {
return SwingComponentFactory.buildCheckBox("FULLY_QUALIFIED_CHECK_BOX", this.fullyQualifiedCheckBoxModel, resourceRepository());
}
private JPanel buildTableSelectionPanel() {
DualListSelectorPanel.Builder builder = new DualListSelectorPanel.Builder();
builder.setAvailableLVM(this.buildSortedExternalTableDescriptionsHolderAdapter(this.availableExternalTableDescriptionsHolder));
builder.setSelectedLVM(this.buildSortedExternalTableDescriptionsHolderAdapter(this.selectedExternalTableDescriptionsHolder));
builder.setAdapter(this.buildExternalTableDescriptionSelectionAdapter());
builder.setListCellRenderer(this.buildExternalTableDescriptionRenderer());
builder.setContext(this.getApplicationContext());
builder.setAvailableListBoxLabelKey("AVAILABLE_TABLES_LIST_BOX_LABEL");
builder.setSelectedListBoxLabelKey("SELECTED_TABLES_LIST_BOX_LABEL");
builder.setSelectButtonToolTipKey("ADD_SELECTED_TABLES_BUTTON.toolTipText");
builder.setDeselectButtonToolTipKey("REMOVE_SELECTED_TABLES_BUTTON.toolTipText");
builder.setHelpTopicID(this.helpTopicId());
return builder.buildPanel();
}
private ListValueModel buildSortedExternalTableDescriptionsHolderAdapter(CollectionValueModel externalTableDescriptionsHolder) {
return new SortedListValueModelAdapter(externalTableDescriptionsHolder, EXTERNAL_TABLE_DESCRIPTION_COMPARATOR);
}
private DualListSelectorPanel.Adapter buildExternalTableDescriptionSelectionAdapter() {
return new DualListSelectorPanel.Adapter() {
public void select(Object item) {
TableImporterDialog.this.availableExternalTableDescriptionsHolder.removeItem(item);
TableImporterDialog.this.selectedExternalTableDescriptionsHolder.addItem(item);
}
public void deselect(Object item) {
TableImporterDialog.this.selectedExternalTableDescriptionsHolder.removeItem(item);
TableImporterDialog.this.availableExternalTableDescriptionsHolder.addItem(item);
}
};
}
private ListCellRenderer buildExternalTableDescriptionRenderer() {
return new SimpleListCellRenderer() {
protected String buildText(Object value) {
return ((ExternalTableDescription) value).getQualifiedName();
}
};
}
// ********** queries **********
/**
* return the appropriate setting to pass in the call to
* java.sql.DatabaseMetaData#getTables(String, String, String, String[])
*/
String catalog() {
String catalog = (String) this.catalogHolder.getValue();
if (catalog == this.ignoreCatalog) {
return null;
}
if (catalog == this.noCatalog) {
return "";
}
return catalog;
}
/**
* return the appropriate setting to pass in the call to
* java.sql.DatabaseMetaData#getTables(String, String, String, String[])
*/
String schemaPattern() {
String schema = (String) this.schemaPatternHolder.getValue();
if (schema == this.ignoreSchema) {
return null;
}
if (schema == this.noSchema) {
return "";
}
return schema;
}
/**
* return the appropriate setting to pass in the call to
* java.sql.DatabaseMetaData#getTables(String, String, String, String[])
*/
String tableNamePattern() {
return (String) this.tableNamePatternHolder.getValue();
}
/**
* return the appropriate setting to pass in the call to
* java.sql.DatabaseMetaData#getTables(String, String, String, String[])
* currently we only allow a single table type per query...
*/
String[] tableTypes() {
String tableType = (String) this.tableTypeHolder.getValue();
if (tableType == this.allTableTypes) {
return null;
}
return new String[] { tableType };
}
/**
* return whether the tables should be imported with
* fully-qualified names
*/
public boolean importsTablesFullyQualified() {
return ((Boolean) this.fullyQualifiedHolder.getValue()).booleanValue();
}
public Collection selectedTables() {
return CollectionTools.list((Iterator) this.selectedExternalTableDescriptionsHolder.getValue());
}
// ********** behavior **********
/**
* Fork a thread to get a list of tables from the database
* that match the user-entered search criteria and put it
* in the list of "available" tables;
* leave the list of "selected" tables intact, allowing the
* user to build up a list over multiple queries
*/
void getTableNamesPressed() {
Thread thread = new Thread(new TableNameQuery(), "Database Table Name Query");
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
/**
* callback to allow thread-safe adding of table names
*/
void addTableNamesCallback(final Collection tableNames) throws InterruptedException, InvocationTargetException {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
TableImporterDialog.this.availableExternalTableDescriptionsHolder.clear();
// add all the tables at once to reduce the sorting effort
TableImporterDialog.this.availableExternalTableDescriptionsHolder.addItems(tableNames);
}
});
}
/**
* query the database for the table names
*/
private class TableNameQuery implements Runnable {
TableNameQuery() {
super();
}
public void run() {
this.setCursor(CursorConstants.WAIT_CURSOR);
WaitDialog waitDialog = this.buildWaitDialog();
launchLater(waitDialog);
Collection externalTableDescriptions = new ArrayList(1000); // start sorta big
try {
for (Iterator stream = this.externalTableDescriptions(); stream.hasNext(); ) {
externalTableDescriptions.add(stream.next());
}
TableImporterDialog.this.addTableNamesCallback(externalTableDescriptions);
} catch (Throwable t) {
throw new RuntimeException(t);
} finally {
waitDialog.dispose();
this.setCursor(CursorConstants.DEFAULT_CURSOR);
}
}
private void setCursor(Cursor cursor) {
TableImporterDialog.this.getWorkbenchContext().getCurrentWindow().setCursor(cursor);
}
private WaitDialog buildWaitDialog() {
return new WaitDialog(
TableImporterDialog.this,
TableImporterDialog.this.resourceRepository().getIcon("database.large"),
TableImporterDialog.this.resourceRepository().getString("GET_TABLE_NAMES_DIALOG.TITLE"),
TableImporterDialog.this.resourceRepository().getString("GET_TABLE_NAMES_DIALOG_MESSAGE")
);
}
private Iterator externalTableDescriptions() {
return TableImporterDialog.this.database.externalTableDescriptions(
TableImporterDialog.this.catalog(),
TableImporterDialog.this.schemaPattern(),
TableImporterDialog.this.tableNamePattern(),
TableImporterDialog.this.tableTypes()
);
}
}
}