Java tutorial
/* * ARX: Powerful Data Anonymization * Copyright (C) 2014 Karol Babioch <karol@babioch.de> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.deidentifier.arx.gui.view.impl.wizard; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.deidentifier.arx.DataType; import org.deidentifier.arx.io.ImportAdapter; import org.deidentifier.arx.io.ImportColumn; import org.deidentifier.arx.io.ImportColumnExcel; import org.deidentifier.arx.io.ImportConfigurationExcel; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; 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.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; /** * Excel page * * This page offers means to import data from an Excel file. It contains * mechanisms to select such a file, and offers the user the ability to choose * the sheet to import from and whether or not the first row contains a header * describing each column. A live preview makes sure the user will immediately * see whether or not his choices make any sense. * * All of the data gathered on this page is stored within {@link ImportWizardModel}. * * This includes: * * <ul> * <li>{@link ImportWizardModel#setWizardColumns(List)}</li> * <li>{@link ImportWizardModel#setFirstRowContainsHeader(boolean)</li> * <li>{@link ImportWizardModel#setFileLocation(String)}</li> * <li>{@link ImportWizardModel#setExcelSheetIndex(int)}</li> * </ul> * * @author Karol Babioch * @author Fabian Prasser */ public class ImportWizardPageExcel extends WizardPage { /** * Label provider for Excel columns * * A new instance of this object will be initiated for each column of * {@link tableViewerPreview}. This class holds the index of the * appropriate column {@link #index}, making sure they will return the * correct value for each column. */ class ExcelColumnLabelProvider extends ColumnLabelProvider { /** * Index of the column this instance is representing */ private int index; /** * Creates new instance of this class for the given index * * @param index Index the instance should be created for */ public ExcelColumnLabelProvider(int index) { this.index = index; } /** * Returns the string value for the given column */ @Override public String getText(Object element) { return ((String[]) element)[index]; } /** * Returns tooltip for each element of given column * * The tooltip contains the current row as well as the column index * itself. */ @Override public String getToolTipText(Object element) { int row = previewData.indexOf(element); return "Row: " + (row + 1) + ", Column: " + (index + 1); } } /** * Reference to the wizard containing this page */ private ImportWizard wizardImport; /** * Columns detected by this page and passed on to {@link ImportWizardModel} */ private ArrayList<ImportWizardModelColumn> wizardColumns; /* Widgets */ private Label lblLocation; private Combo comboLocation; private Button btnChoose; private Button btnContainsHeader; private Combo comboSheet; private Label lblSheet; private Table tablePreview; private TableViewer tableViewerPreview; /** * Preview data */ ArrayList<String[]> previewData = new ArrayList<String[]>(); /** * Workbook * * Either HSSFWorkbook or XSSFWorkbook, depending upon file type */ private Workbook workbook; /** Input stream*/ private InputStream stream; /** * Creates a new instance of this page and sets its title and description * * @param wizardImport Reference to wizard containing this page */ public ImportWizardPageExcel(ImportWizard wizardImport) { super("WizardImportExcelPage"); setTitle("Excel"); setDescription("Please provide the information requested below"); this.wizardImport = wizardImport; } /** * Creates the design of this page * * This adds all the controls to the page along with their listeners. * * @note {@link #tablePreview} is not visible until a file is loaded. */ public void createControl(Composite parent) { Composite container = new Composite(parent, SWT.NULL); setControl(container); container.setLayout(new GridLayout(3, false)); /* Location label */ lblLocation = new Label(container, SWT.NONE); lblLocation.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); lblLocation.setText("Location"); /* Combo box for selection of file */ comboLocation = new Combo(container, SWT.READ_ONLY); comboLocation.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); comboLocation.addSelectionListener(new SelectionAdapter() { /** * Reads the sheets and selects active one */ @Override public void widgetSelected(SelectionEvent arg0) { /* Try to read in sheets */ try { readSheets(); } catch (IOException e) { setErrorMessage("Couldn't read sheets from file"); } /* Make widgets visible */ comboSheet.setVisible(true); lblSheet.setVisible(true); btnContainsHeader.setVisible(true); /* Select active sheet and notify comboSheet about change */ comboSheet.select(workbook.getActiveSheetIndex()); comboSheet.notifyListeners(SWT.Selection, null); } }); /* Button to open file selection dialog */ btnChoose = new Button(container, SWT.NONE); btnChoose.setText("Browse..."); btnChoose.addSelectionListener(new SelectionAdapter() { /** * Opens a file selection dialog for Excel files * * Both XLS and XLSX files can be selected. If a valid file was * selected, it is added to {@link #comboLocation} when it wasn't * already there. In either case it gets preselected. * * @see {@link Controller#actionShowOpenFileDialog(String)} */ @Override public void widgetSelected(SelectionEvent arg0) { /* Open file dialog */ final String path = wizardImport.getController().actionShowOpenFileDialog(getShell(), "*.xls;*.xlsx"); if (path == null) { return; } /* Check whether path was already added */ if (comboLocation.indexOf(path) == -1) { comboLocation.add(path, 0); } /* Select path and notify comboLocation about change */ comboLocation.select(comboLocation.indexOf(path)); comboLocation.notifyListeners(SWT.Selection, null); } }); /* Sheet label */ lblSheet = new Label(container, SWT.NONE); lblSheet.setVisible(false); lblSheet.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); lblSheet.setText("Sheet"); /* Sheet combobox */ comboSheet = new Combo(container, SWT.READ_ONLY); comboSheet.setVisible(false); comboSheet.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); comboSheet.addSelectionListener(new SelectionAdapter() { /** * (Re-)Evaluate page */ @Override public void widgetSelected(final SelectionEvent arg0) { evaluatePage(); } }); /* Place holders */ new Label(container, SWT.NONE); new Label(container, SWT.NONE); /* Contains header button */ btnContainsHeader = new Button(container, SWT.CHECK); btnContainsHeader.setVisible(false); btnContainsHeader.setText("First row contains column names"); btnContainsHeader.setSelection(true); btnContainsHeader.addSelectionListener(new SelectionAdapter() { /** * (Re-)Evaluate page */ @Override public void widgetSelected(SelectionEvent arg0) { evaluatePage(); } }); /* Place holders */ new Label(container, SWT.NONE); new Label(container, SWT.NONE); new Label(container, SWT.NONE); new Label(container, SWT.NONE); /* Preview table viewer */ tableViewerPreview = new TableViewer(container, SWT.BORDER | SWT.FULL_SELECTION); tableViewerPreview.setContentProvider(new ArrayContentProvider()); /* Actual table for {@link #tableViewerPreview} */ tablePreview = tableViewerPreview.getTable(); GridData gd_tablePreview = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1); gd_tablePreview.heightHint = 150; tablePreview.setLayoutData(gd_tablePreview); tablePreview.setLinesVisible(true); tablePreview.setVisible(false); /* Set page to incomplete by default */ setPageComplete(false); } /** * Evaluates the page * * This checks whether the current settings on the page make any sense * and applies them appropriately. It basically checks tries to read in * the preview data {@link #readPreview()}. * * If everything is fine, the settings are being put into the appropriate * data container {@link ImportWizardModel} and the current page is marked as * complete by invoking {@link #setPageComplete(boolean)}. Otherwise an * error message is set, which will make sure the user is informed about * the reason for the error. */ private void evaluatePage() { setPageComplete(false); setErrorMessage(null); tablePreview.setVisible(false); if (comboLocation.getText().equals("")) { return; } try { readPreview(); } catch (IOException | IllegalArgumentException e) { setErrorMessage(e.getMessage()); return; } /* Put data into container */ ImportWizardModel data = wizardImport.getData(); data.setWizardColumns(wizardColumns); data.setPreviewData(previewData); data.setFirstRowContainsHeader(btnContainsHeader.getSelection()); data.setFileLocation(comboLocation.getText()); data.setExcelSheetIndex(comboSheet.getSelectionIndex()); /* Mark page as completed */ setPageComplete(true); } /** * Reads in preview data * * This goes through up to {@link ImportWizardModel#previewDataMaxLines} lines * within the appropriate file and reads them in. It uses * {@link ImportAdapter} in combination with * {@link ImportConfigurationExcel} to actually read in the data. */ private void readPreview() throws IOException { /* Reset preview data */ previewData.clear(); /* Parameters from the user interface */ final String location = comboLocation.getText(); final int sheetIndex = comboSheet.getSelectionIndex(); final boolean containsHeader = btnContainsHeader.getSelection(); /* Variables needed for processing */ Sheet sheet = workbook.getSheetAt(sheetIndex); Iterator<Row> rowIterator = sheet.iterator(); ImportConfigurationExcel config = new ImportConfigurationExcel(location, sheetIndex, containsHeader); wizardColumns = new ArrayList<ImportWizardModelColumn>(); /* Check whether there is at least one row in sheet and retrieve it */ if (!rowIterator.hasNext()) { throw new IOException("Sheet contains no actual data"); } /* Get first row */ Row firstRow = rowIterator.next(); /* Check whether there is at least one column in row */ if (firstRow.getPhysicalNumberOfCells() < 1) { throw new IOException("First row contains no data"); } /* Iterate over columns and add them */ for (int i = 0; i < firstRow.getPhysicalNumberOfCells(); i++) { ImportColumn column = new ImportColumnExcel(i, DataType.STRING); ImportWizardModelColumn wizardColumn = new ImportWizardModelColumn(column); wizardColumns.add(wizardColumn); config.addColumn(column); } /* Create adapter to import data with given configuration */ ImportAdapter importAdapter = ImportAdapter.create(config); /* Get up to {ImportData#previewDataMaxLines} lines for previewing */ int count = 0; while (importAdapter.hasNext() && (count <= ImportWizardModel.previewDataMaxLines)) { previewData.add(importAdapter.next()); count++; } /* Remove first entry as it always contains name of columns */ previewData.remove(0); /* Check whether there is actual any data */ if (previewData.size() == 0) { throw new IOException("No actual data in file"); } /* Disable redrawing once redesign is finished */ tablePreview.setRedraw(false); /* Remove all of the old columns */ while (tablePreview.getColumnCount() > 0) { tablePreview.getColumns()[0].dispose(); } /* Add new columns */ for (ImportWizardModelColumn column : wizardColumns) { TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewerPreview, SWT.NONE); tableViewerColumn.setLabelProvider( new ExcelColumnLabelProvider(((ImportColumnExcel) column.getColumn()).getIndex())); TableColumn tableColumn = tableViewerColumn.getColumn(); tableColumn.setWidth(100); if (btnContainsHeader.getSelection()) { tableColumn.setText(column.getColumn().getAliasName()); tableColumn.setToolTipText("Column #" + ((ImportColumnExcel) column.getColumn()).getIndex()); } ColumnViewerToolTipSupport.enableFor(tableViewerPreview, ToolTip.NO_RECREATE); } /* Setup preview table */ tableViewerPreview.setInput(previewData); tablePreview.setHeaderVisible(btnContainsHeader.getSelection()); tablePreview.setVisible(true); tablePreview.layout(); tablePreview.setRedraw(true); } /** * Reads in the available sheets from file * * This reads in the available sheets from the file chosen at * {@link #comboLocation} and adds them as items to {@link #comboSheet}. */ private void readSheets() throws IOException { /* Remove previous items */ comboSheet.removeAll(); /* Get workbook */ try { try { if (stream != null) stream.close(); } catch (Exception e) { /* Die silently*/ } stream = new FileInputStream(comboLocation.getText()); workbook = WorkbookFactory.create(stream); } catch (InvalidFormatException e) { throw new IOException("File format invalid"); } /* Add all sheets to combo */ for (int i = 0; i < workbook.getNumberOfSheets(); i++) { comboSheet.add(workbook.getSheetName(i)); } } @Override public void setVisible(boolean value) { super.setVisible(value); try { if (stream != null) stream.close(); } catch (Exception e) { /* Die silently*/ } } }