/* * ARX: Powerful Data Anonymization * Copyright 2012 - 2017 Fabian Prasser, Florian Kohlmayer and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.deidentifier.arx.gui.view.impl.wizard; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import org.deidentifier.arx.DataType; import org.deidentifier.arx.gui.resources.Resources; import org.deidentifier.arx.gui.view.SWTUtil; import org.deidentifier.arx.io.IOUtil; import org.deidentifier.arx.io.ImportColumnJDBC; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.window.ToolTip; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; /** * Table overview page * * This pages gives the user an overview of the detected tables and allows him * to select the desired one by clicking on it. The tables itself are retrieved * from {@link ImportData#getJdbcTables()()}. The selected one will be assigned * via {@link ImportWizardModel#setSelectedJdbcTable(String)} along with the * detected columns for this table using {@link ImportWizardModel#setWizardColumns(List)} and its preview data {@link ImportWizardModel#setPreviewData(List)}. * * @author Karol Babioch * @author Fabian Prasser */ public class ImportWizardPageTable extends WizardPage { /** * Returns a human readable string representation of <code>rows</code> * * This converts rows into a human readable string, e.g. 1000000 gets * converted to 1M. * * The code is based upon <a href="http://bit.ly/1m4UetX">this</a> snippet. * * @param rows The number of rows to be converted * * @return Human readable string representation of <code>rows</code> */ private static String getHumanReadableCount(long rows) { int unit = 1000; if (rows < unit) { return new Long(rows).toString(); } else { int exp = (int) (Math.log(rows) / Math.log(unit)); char pre = "kMGTPE".charAt(exp - 1); //$NON-NLS-1$ return String.format("%.1f%s", rows / Math.pow(unit, exp), pre); //$NON-NLS-1$ } } /** Reference to the wizard containing this page. */ private ImportWizard wizardImport; /** SWT Widgets */ private Table table; /** SWT Widgets */ private TableViewer tableViewer; /** * Creates a new instance of this page and sets its title and description. * * @param wizardImport Reference to wizard containing this page */ public ImportWizardPageTable(ImportWizard wizardImport) { super("WizardImportTablePage"); //$NON-NLS-1$ this.wizardImport = wizardImport; this.setTitle(Resources.getMessage("ImportWizardPageTable.3")); //$NON-NLS-1$ this.setDescription(Resources.getMessage("ImportWizardPageTable.4")); //$NON-NLS-1$ } /** * Creates the design of this page along with the appropriate listeners. * * @param parent */ public void createControl(Composite parent) { Composite container = new Composite(parent, SWT.NULL); setControl(container); container.setLayout(new GridLayout(1, false)); /* TableViewer for the detected tables */ tableViewer = SWTUtil.createTableViewer(container, SWT.BORDER | SWT.FULL_SELECTION); tableViewer.setContentProvider(new ArrayContentProvider()); ColumnViewerToolTipSupport.enableFor(tableViewer, ToolTip.NO_RECREATE); tableViewer.addSelectionChangedListener(new ISelectionChangedListener() { /** * Reads in the columns and preview data for selected table */ @Override public void selectionChanged(SelectionChangedEvent arg0) { /* Save selected table */ int index = table.getSelectionIndex(); String selectedTable = wizardImport.getData() .getJdbcTables() .get(index); wizardImport.getData().setSelectedJdbcTable(selectedTable); readColumns(); setPageComplete(readPreview()); } }); /* Table for {@link #tableViewer} */ table = tableViewer.getTable(); table.setHeaderVisible(true); table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); /* Column for table names */ TableViewerColumn tableViewerColumnName = new TableViewerColumn(tableViewer, SWT.NONE); tableViewerColumnName.setLabelProvider(new ColumnLabelProvider() { /** Returns table name */ @Override public String getText(Object element) { return (String) element; } }); TableColumn tblclmnColumnName = tableViewerColumnName.getColumn(); tblclmnColumnName.setToolTipText(Resources.getMessage("ImportWizardPageTable.5")); //$NON-NLS-1$ tblclmnColumnName.setWidth(300); tblclmnColumnName.setText(Resources.getMessage("ImportWizardPageTable.6")); //$NON-NLS-1$ TableViewerColumn tableViewerColumnColumns = new TableViewerColumn(tableViewer, SWT.NONE); tableViewerColumnColumns.setLabelProvider(new ColumnLabelProvider() { /** * Returns number of columns for table * * If the number of columns couldn't be determined, three question * marks are returned. */ @Override public String getText(Object element) { int columns = getNumberOfColumns((String) element); if (columns != -1) { return "" + columns; //$NON-NLS-1$ } else { return "???"; //$NON-NLS-1$ } } }); TableColumn tblclmnColumns = tableViewerColumnColumns.getColumn(); tblclmnColumns.setToolTipText(Resources.getMessage("ImportWizardPageTable.9")); //$NON-NLS-1$ tblclmnColumns.setWidth(100); tblclmnColumns.setText(Resources.getMessage("ImportWizardPageTable.10")); //$NON-NLS-1$ TableViewerColumn tableViewerColumnRows = new TableViewerColumn(tableViewer, SWT.NONE); tableViewerColumnRows.setLabelProvider(new ColumnLabelProvider() { /** * Returns number of rows for table * * If the number of rows couldn't be determined, three question * marks are returned. */ @Override public String getText(Object element) { long rows = getNumberOfRows((String) element); if (rows != -1) { return " ~ " + getHumanReadableCount(rows); //$NON-NLS-1$ } else { return "???"; //$NON-NLS-1$ } } /** * Returns the exact number of rows as tooltip * * This will return the exact number of rows for tables with a * row count greater than thousand, as the column itself will * only show a human readable string. * * @see #getText(Object) * @see #getNumberOfRows(String) */ @Override public String getToolTipText(Object element) { long rows = getNumberOfRows((String) element); if (rows > 1000) { return "" + rows; //$NON-NLS-1$ } else { return null; } } }); TableColumn tblclmnRows = tableViewerColumnRows.getColumn(); tblclmnRows.setToolTipText(Resources.getMessage("ImportWizardPageTable.14")); //$NON-NLS-1$ tblclmnRows.setWidth(100); tblclmnRows.setText(Resources.getMessage("ImportWizardPageTable.15")); //$NON-NLS-1$ setPageComplete(false); } /** * Applies previously detected tables to {@link #tableViewer}. * * @param visible */ @Override public void setVisible(boolean visible) { super.setVisible(visible); if (visible) { tableViewer.setInput(wizardImport.getData().getJdbcTables()); /* Mark page as complete when table has been selected before */ if (wizardImport.getData().getSelectedJdbcTable() != null) { setPageComplete(true); } } else { setPageComplete(false); } } /** * Gets the number of columns for given table * * This uses the JDBC connection {@link ImportWizardModel#getJdbcConnection()} to determine the number of * columns for given table. * * @param table * Table number of rows should be returned for * * @return Number of rows for given table, -1 in case of error */ private int getNumberOfColumns(String selectedTable) { ResultSet rs = null; int i = 0; try { String table = selectedTable; String schema = null; if (selectedTable.contains(".")) { table = selectedTable.split("\\.")[1]; schema = selectedTable.split("\\.")[0]; } Connection connection = wizardImport.getData().getJdbcConnection(); rs = connection.getMetaData().getColumns(null, schema, table, null); while (rs.next()) { i++; } return i; } catch (SQLException e) { /* Ignore silently */ } finally { try { if (rs != null) { rs.close(); } } catch (SQLException e) { /* Ignore silently */ } } return -1; } /** * Gets the number of rows for given table * * This uses the JDBC connection {@link ImportWizardModel#getJdbcConnection()} to determine the number of * rows for given table. * * @param table * Table number of rows should be returned for * * @return Number of rows for given table, -1 in case of error */ private long getNumberOfRows(String table) { Statement statement = null; ResultSet resultSet = null; try { statement = wizardImport.getData() .getJdbcConnection() .createStatement(); statement.execute("SELECT COUNT(*) FROM " + table); //$NON-NLS-1$ resultSet = statement.getResultSet(); if (resultSet.next()) { return resultSet.getLong(1); } } catch (SQLException e) { /* Ignore silently */ } finally { try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e) { /* Ignore silently */ } try { if (statement != null) { statement.close(); } } catch (SQLException e) { /* Ignore silently */ } } return -1L; } /** * Reads in the columns of currently selected table * * If this can be performed successful, the columns will be made available * for the next page by {@link ImportWizardModel#setWizardColumns(List)}. * Otherwise an appropriate error message is set. */ private void readColumns() { String selectedTable = wizardImport.getData().getSelectedJdbcTable(); Connection connection = wizardImport.getData().getJdbcConnection(); List<ImportWizardModelColumn> columns = new ArrayList<ImportWizardModelColumn>(); int i = 0; ResultSet rs = null; try { String table = selectedTable; String schema = null; if (selectedTable.contains(".")) { table = selectedTable.split("\\.")[1]; schema = selectedTable.split("\\.")[0]; } rs = connection.getMetaData().getColumns(null, schema, table, null); while (rs.next()) { ImportColumnJDBC column = new ImportColumnJDBC(i++, IOUtil.trim(rs.getString("COLUMN_NAME")), //$NON-NLS-1$ DataType.STRING); columns.add(new ImportWizardModelColumn(column)); } } catch (SQLException e) { setErrorMessage(Resources.getMessage("ImportWizardPageTable.17")); //$NON-NLS-1$ } finally { try { if (rs != null) { rs.close(); } } catch (SQLException e) { /* Ignore silently */ } } wizardImport.getData().setWizardColumns(columns); } /** * Reads in the preview data for currently selected table * * If this can be performed successful, the preview data will be made * available for the following pages by {@link ImportWizardModel#setPreviewData(List)}. * Otherwise an appropriate error message is set. */ private boolean readPreview() { String selectedTable = wizardImport.getData().getSelectedJdbcTable(); Connection connection = wizardImport.getData().getJdbcConnection(); Statement statement = null; ResultSet rs = null; try { statement = connection.createStatement(); statement.setMaxRows(ImportWizardModel.PREVIEW_MAX_LINES); statement.execute("SELECT * FROM " + selectedTable); //$NON-NLS-1$ rs = statement.getResultSet(); List<String[]> previewData = new ArrayList<String[]>(); while (rs.next()) { String[] previewRow = new String[rs.getMetaData().getColumnCount()]; for (int j = 0; j < previewRow.length; j++) { previewRow[j] = IOUtil.trim(rs.getString(j + 1)); } previewData.add(previewRow); } wizardImport.getData().setPreviewData(previewData); if (previewData.isEmpty()) { setErrorMessage(Resources.getMessage("ImportWizardPageTable.22")); //$NON-NLS-1$ return false; } else { setErrorMessage(null); setMessage(Resources.getMessage("ImportWizardPageTable.23"), INFORMATION); //$NON-NLS-1$ return true; } } catch (SQLException e) { wizardImport.getData().setPreviewData(new ArrayList<String[]>()); setErrorMessage(Resources.getMessage("ImportWizardPageTable.21")); //$NON-NLS-1$ return false; } finally { try { if (rs != null) { rs.close(); } } catch (SQLException e) { /* Ignore silently */ } try { if (statement != null) { statement.close(); } } catch (SQLException e) { /* Ignore silently */ } } } }