From 17e38e3d4016d7b24426a630b88551db8f52bf8c Mon Sep 17 00:00:00 2001 From: EHJ-52n Date: Fri, 25 Apr 2014 11:06:39 +0200 Subject: [PATCH 1/2] Add type CsvData for better handling incl. tests --- .../org/n52/sos/importer/model/CsvData.java | 83 +++++++++++++++++ .../n52/sos/importer/model/CsvDataTest.java | 88 +++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 52n-sos-importer-core/src/main/java/org/n52/sos/importer/model/CsvData.java create mode 100644 52n-sos-importer-core/src/test/java/org/n52/sos/importer/model/CsvDataTest.java diff --git a/52n-sos-importer-core/src/main/java/org/n52/sos/importer/model/CsvData.java b/52n-sos-importer-core/src/main/java/org/n52/sos/importer/model/CsvData.java new file mode 100644 index 00000000..54a89d55 --- /dev/null +++ b/52n-sos-importer-core/src/main/java/org/n52/sos/importer/model/CsvData.java @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2014 + * by 52 North Initiative for Geospatial Open Source Software GmbH + * + * Contact: Andreas Wytzisk + * 52 North Initiative for Geospatial Open Source Software GmbH + * Martin-Luther-King-Weg 24 + * 48155 Muenster, Germany + * info@52north.org + * + * This program is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * This program is distributed WITHOUT ANY WARRANTY; even without 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 (see gnu-gpl v2.txt). If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA or + * visit the Free Software Foundation web page, http://www.fsf.org. + */ +package org.n52.sos.importer.model; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * This class holds the data of an CSV file incl. work arounds for bad + * formatted files: + * + * + * @since 0.4.0 + * @author Eike Hinderk Jürrens + */ +public class CsvData { + + private List lines = new LinkedList<>(); + private int columns = 0; + + public void setLines(final List lines) { + this.lines = lines; + columns = 0; + if (lines != null) { + for (final String[] strings : lines) { + if (columns < strings.length) { + columns = strings.length; + } + } + } + } + + public int getRowCount() { + if (lines == null) { + return 0; + } + return lines.size(); + } + + public int getColumnCount() { + return columns; + } + + public String[] getLine(final int i) { + final String[] extended = new String[columns]; + if (lines == null) { + Arrays.fill(extended, ""); + return extended; + } + String[] tmp = lines.get(i); + if (tmp.length < columns) { + Arrays.fill(extended, ""); + for (int j = 0; j < tmp.length; j++) { + extended[j] = tmp[j]; + } + tmp = extended; + } + return tmp; + } + +} diff --git a/52n-sos-importer-core/src/test/java/org/n52/sos/importer/model/CsvDataTest.java b/52n-sos-importer-core/src/test/java/org/n52/sos/importer/model/CsvDataTest.java new file mode 100644 index 00000000..6f9edd61 --- /dev/null +++ b/52n-sos-importer-core/src/test/java/org/n52/sos/importer/model/CsvDataTest.java @@ -0,0 +1,88 @@ +/** + * Copyright (C) 2014 + * by 52 North Initiative for Geospatial Open Source Software GmbH + * + * Contact: Andreas Wytzisk + * 52 North Initiative for Geospatial Open Source Software GmbH + * Martin-Luther-King-Weg 24 + * 48155 Muenster, Germany + * info@52north.org + * + * This program is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * This program is distributed WITHOUT ANY WARRANTY; even without 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 (see gnu-gpl v2.txt). If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA or + * visit the Free Software Foundation web page, http://www.fsf.org. + */ +package org.n52.sos.importer.model; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.LinkedList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +public class CsvDataTest { + + private CsvData data; + + @Before + public void setupData() { + data = new CsvData(); + } + + @Test + public void shouldReturnRowCountZeroIfLinesNotSet() { + assertThat(data.getRowCount(), is(0)); + } + + @Test + public void shouldReturnColumnCountZeroIfLinesNotSet() { + assertThat(data.getColumnCount(), is(0)); + } + + @Test + public void shouldReturnZeroForCountsIfLinesIsNull() { + data.setLines(null); + + assertThat(data.getRowCount(), is(0)); + assertThat(data.getColumnCount(), is(0)); + } + + @Test + public void shouldReturnCorrectColumnCount() { + final List testData = new LinkedList<>(); + testData.add(new String[] {"col1","col2"}); + testData.add(new String[] {"col1"}); + testData.add(new String[] {"col1","col2", "col3"}); + data.setLines(testData); + + assertThat(data.getColumnCount(), is(3)); + assertThat(data.getRowCount(), is(3)); + } + + @Test + public void shouldReturnLineFilledWithEmptyStringsIfColumnContainedLessValuesThanColumnCount() { + final List testData = new LinkedList<>(); + testData.add(new String[] {"col1","col2"}); + testData.add(new String[] {"col1"}); + testData.add(new String[] {"col1","col2", "col3"}); + data.setLines(testData); + + assertThat(data.getLine(1).length, is(data.getColumnCount())); + for (int i = 1; i < data.getLine(1).length; i++) { + final String string = data.getLine(1)[i]; + assertThat(string, is("")); + } + } +} From 219cdfaf8ccac5029e7061cb5dc6882b54752cf2 Mon Sep 17 00:00:00 2001 From: EHJ-52n Date: Fri, 25 Apr 2014 11:07:55 +0200 Subject: [PATCH 2/2] Use new type CsvData to fix GUI bug with CSV data containing lines with different column counts Fixes 52North/sos-importer#20 --- .../importer/controller/Step2Controller.java | 20 +- .../importer/controller/TableController.java | 321 +++++++++--------- 2 files changed, 172 insertions(+), 169 deletions(-) diff --git a/52n-sos-importer-core/src/main/java/org/n52/sos/importer/controller/Step2Controller.java b/52n-sos-importer-core/src/main/java/org/n52/sos/importer/controller/Step2Controller.java index ed0478f0..b8a0c2be 100644 --- a/52n-sos-importer-core/src/main/java/org/n52/sos/importer/controller/Step2Controller.java +++ b/52n-sos-importer-core/src/main/java/org/n52/sos/importer/controller/Step2Controller.java @@ -26,11 +26,11 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; -import java.util.List; import javax.swing.JPanel; import org.n52.sos.importer.Constants; +import org.n52.sos.importer.model.CsvData; import org.n52.sos.importer.model.Step2Model; import org.n52.sos.importer.model.StepModel; import org.n52.sos.importer.view.Step2Panel; @@ -87,7 +87,7 @@ public boolean isFinished() { @Override public StepController getNextStepController() { - final Object[][] content = parseCSVFile(); + final CsvData content = parseCSVFile(); TableController.getInstance().setContent(content); TableController.getInstance().setFirstLineWithData( step2Model.getFirstLineWithData()); @@ -234,8 +234,8 @@ public StepModel getModel() { return step2Model; } - private Object[][] parseCSVFile() { - Object[][] content = null; + private CsvData parseCSVFile() { + final CsvData content = new CsvData(); String csvFileContent = step2Model.getCSVFileContent(); String separator = step2Model.getColumnSeparator(); final String quoteChar = step2Model.getCommentIndicator(); @@ -258,16 +258,8 @@ private Object[][] parseCSVFile() { } final StringReader sr = new StringReader(csvFileContent); try (CSVReader reader = new CSVReader(sr, separator.charAt(0), quoteChar.charAt(0), escape.charAt(0))){ - final List lines = reader.readAll(); - final int rows = lines.size(); - final String[] firstLine = lines.get(0); - final int columns = firstLine.length; - content = new String[rows][columns]; - - for (int i = 0; i < rows; i++) { - content[i] = lines.get(i); - } - } catch (final IOException e) { + content.setLines(reader.readAll()); + } catch (final IOException e) { logger.error("Error while parsing CSV file.", e); } return content; diff --git a/52n-sos-importer-core/src/main/java/org/n52/sos/importer/controller/TableController.java b/52n-sos-importer-core/src/main/java/org/n52/sos/importer/controller/TableController.java index d14b1156..3bc25449 100644 --- a/52n-sos-importer-core/src/main/java/org/n52/sos/importer/controller/TableController.java +++ b/52n-sos-importer-core/src/main/java/org/n52/sos/importer/controller/TableController.java @@ -37,6 +37,7 @@ import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; +import org.n52.sos.importer.model.CsvData; import org.n52.sos.importer.model.table.Cell; import org.n52.sos.importer.model.table.Column; import org.n52.sos.importer.model.table.Row; @@ -49,84 +50,86 @@ * */ public class TableController { - + private static TableController instance = null; - + public static final int COLUMNS = 1; public static final int ROWS = 2; public static final int CELLS = 3; - - private TablePanel tableView; - - private JTable table; - + + private final TablePanel tableView; + + private final JTable table; + private SingleSelectionListener singleSelectionListener; - + private MultipleSelectionListener multipleSelectionListener; - - private ColoredTableCellRenderer tableMarker; - + + private final ColoredTableCellRenderer tableMarker; + private int tableSelectionMode; - + private int orientation = COLUMNS; - + private static int firstLineWithData = -1; - + private final Color markingColor = Color.lightGray; - private TableController(int firstLineWithData) { + private TableController(final int firstLineWithData) { TableController.firstLineWithData = firstLineWithData; - this.tableView = TablePanel.getInstance(); - this.table = tableView.getTable(); - this.tableMarker = new ColoredTableCellRenderer(); + tableView = TablePanel.getInstance(); + table = tableView.getTable(); + tableMarker = new ColoredTableCellRenderer(); // table.setDefaultRenderer(Object.class, null); table.setDefaultRenderer(Object.class, tableMarker); table.getSelectionModel().addListSelectionListener(new RowSelectionListener()); table.getColumnModel().getSelectionModel() .addListSelectionListener(new ColumnSelectionListener()); - this.allowMultipleSelection(); + allowMultipleSelection(); } public static TableController getInstance() { - if (instance == null) + if (instance == null) { instance = new TableController(-1); + } return instance; - } - - public void setContent(Object[][] content) { - DefaultTableModel dtm = new EditableTableModel(false); + } + + public void setContent(final CsvData content) { + final DefaultTableModel dtm = new EditableTableModel(false); - int rows = content.length; - int columns = content[0].length; + final int rows = content.getRowCount(); + final int columns = content.getColumnCount(); dtm.setColumnCount(columns); - - String[] columnIdentifiers = new String[columns]; - for (int i = 0; i < columnIdentifiers.length; i++) + + final String[] columnIdentifiers = new String[columns]; + for (int i = 0; i < columnIdentifiers.length; i++) { columnIdentifiers[i] = "n/a"; + } dtm.setColumnIdentifiers(columnIdentifiers); for (int i = 0; i < rows; i++) { - dtm.addRow(content[i]); + dtm.addRow(content.getLine(i)); } table.setModel(dtm); } - - public void setColumnHeading(int column, String heading) { + + public void setColumnHeading(final int column, final String heading) { table.getColumnModel().getColumn(column).setHeaderValue(heading); } - + public void allowSingleSelection() { table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } - + public void allowMultipleSelection() { table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); } - - public void setTableSelectionMode(int tableSelectionMode) { + + public void setTableSelectionMode(final int tableSelectionMode) { this.tableSelectionMode = tableSelectionMode; - + switch(tableSelectionMode) { case ROWS: table.setColumnSelectionAllowed(false); @@ -148,153 +151,158 @@ public void setTableSelectionMode(int tableSelectionMode) { break; } } - + public int getTableSelectionMode() { return tableSelectionMode; } - public void selectColumn(int number) { + public void selectColumn(final int number) { table.addColumnSelectionInterval(number, number); } - - public void selectRow(int number) { + + public void selectRow(final int number) { table.addRowSelectionInterval(number, number); } - - public void deselectColumn(int number) { + + public void deselectColumn(final int number) { table.removeColumnSelectionInterval(number, number); } - - public void deselectRow(int number) { + + public void deselectRow(final int number) { table.removeRowSelectionInterval(number, number); } - + public void deselectAllColumns() { - int columns = table.getColumnCount() - 1; + final int columns = table.getColumnCount() - 1; table.removeColumnSelectionInterval(0, columns); } - + public void deselectAllRows() { - int rows = table.getRowCount() - 1; + final int rows = table.getRowCount() - 1; table.removeColumnSelectionInterval(0, rows); } - + public void turnSelectionOff() { table.setRowSelectionAllowed(false); table.setColumnSelectionAllowed(false); table.setCellSelectionEnabled(false); table.setFocusable(false); } - + public void turnSelectionOn() { table.setRowSelectionAllowed(true); table.setColumnSelectionAllowed(true); table.setCellSelectionEnabled(true); table.setFocusable(true); } - + public int[] getSelectedColumns() { return table.getSelectedColumns(); } - + public int getSelectedColumn() { return table.getSelectedColumn(); } - + public int[] getSelectedRows() { return table.getSelectedRows(); } - + public int getSelectedRow() { return table.getSelectedRow(); } - + public List getMarkedValues() { - ArrayList values = new ArrayList(); - + final ArrayList values = new ArrayList(); + switch(tableSelectionMode) { - case COLUMNS: - int rowCount = table.getRowCount(); - - for (Column c: tableMarker.getColumns()) - for (int i = 0; i < rowCount; i++) + case COLUMNS: + final int rowCount = table.getRowCount(); + + for (final Column c: tableMarker.getColumns()) { + for (int i = 0; i < rowCount; i++) { values.add((String)table.getValueAt(i, c.getNumber())); - + } + } + break; - case ROWS: - int columnCount = table.getColumnCount(); - - for (Row r: tableMarker.getRows()) - for (int i = 0; i < columnCount; i++) + case ROWS: + final int columnCount = table.getColumnCount(); + + for (final Row r: tableMarker.getRows()) { + for (int i = 0; i < columnCount; i++) { values.add((String)table.getValueAt(r.getNumber(), i)); + } + } break; case CELLS: - for (Cell c: tableMarker.getCells()) + for (final Cell c: tableMarker.getCells()) { values.add(getValueAt(c)); + } break; } return values; } - + public String getSelectedCellValue() { - int column = table.getSelectedColumn(); - int row = table.getSelectedRow(); + final int column = table.getSelectedColumn(); + final int row = table.getSelectedRow(); return (String)table.getValueAt(row, column); } - - public String getValueAt(Cell c) { + + public String getValueAt(final Cell c) { return (String) table.getValueAt(c.getRow(), c.getColumn()); } - - public String getValueAt(int row, int column) { + + public String getValueAt(final int row, final int column) { return (String) table.getValueAt(row, column); } - + public int getRowCount() { return table.getRowCount(); } - + public int getColumnCount() { return table.getColumnCount(); } - - public void mark(Column c) { + + public void mark(final Column c) { tableMarker.addColumn(c); } - - public void mark(Row r) { + + public void mark(final Row r) { tableMarker.addRow(r); } - - public void mark(Cell c) { + + public void mark(final Cell c) { tableMarker.addCell(c); } - + public void clearMarkedTableElements() { tableMarker.removeTableElements(); } - public void addSingleSelectionListener(SingleSelectionListener singleSelectionListener) { + public void addSingleSelectionListener(final SingleSelectionListener singleSelectionListener) { this.singleSelectionListener = singleSelectionListener; - } - - public void addMultipleSelectionListener(MultipleSelectionListener multipleSelectionListener) { + } + + public void addMultipleSelectionListener(final MultipleSelectionListener multipleSelectionListener) { this.multipleSelectionListener = multipleSelectionListener; } - + public void removeMultipleSelectionListener() { - this.multipleSelectionListener = null; + multipleSelectionListener = null; } - - public void setOrientation(int orientation) { + + public void setOrientation(final int orientation) { this.orientation = orientation; } public int getOrientation() { return orientation; - } - + } + public String getOrientationString() { switch(orientation) { case COLUMNS: return Lang.l().column(); //"column"; @@ -304,120 +312,123 @@ public String getOrientationString() { } private class ColoredTableCellRenderer extends DefaultTableCellRenderer { - + private static final long serialVersionUID = 1L; - - private HashSet columns; - - private HashSet rows; - - private HashSet cells; - + + private final HashSet columns; + + private final HashSet rows; + + private final HashSet cells; + public ColoredTableCellRenderer() { columns = new HashSet(); rows = new HashSet(); cells = new HashSet(); } - - public void addColumn(Column c) { + + public void addColumn(final Column c) { columns.add(c); } - + public HashSet getColumns() { return columns; } - - public void addRow(Row r) { + + public void addRow(final Row r) { rows.add(r); } - + public HashSet getRows() { return rows; } - - public void addCell(Cell c) { + + public void addCell(final Cell c) { cells.add(c); } - + public HashSet getCells() { return cells; } - + public void removeTableElements() { columns.clear(); rows.clear(); cells.clear(); } - + @Override - public Component getTableCellRendererComponent (JTable table, Object value, boolean selected, boolean focused, int row, int column) { + public Component getTableCellRendererComponent (final JTable table, final Object value, final boolean selected, final boolean focused, final int row, final int column) { setEnabled(table == null || table.isEnabled()); - - if (rows.contains(new Row(row)) || + + if (rows.contains(new Row(row)) || columns.contains(new Column(column,firstLineWithData)) || cells.contains(new Cell(row, column))) { setBackground(markingColor); - } - else setBackground(null); - + } else { + setBackground(null); + } + super.getTableCellRendererComponent(table, value, selected, focused, row, column); return this; } } - + private class EditableTableModel extends DefaultTableModel { - + private static final long serialVersionUID = 1L; - private boolean editable; - - public EditableTableModel(boolean editable) { + private final boolean editable; + + public EditableTableModel(final boolean editable) { super(); this.editable = editable; } - + @Override - public boolean isCellEditable(int row, int column) { + public boolean isCellEditable(final int row, final int column) { return editable; } } - + private class ColumnSelectionListener implements ListSelectionListener { @Override - public void valueChanged(ListSelectionEvent arg0) { + public void valueChanged(final ListSelectionEvent arg0) { if (table.getColumnSelectionAllowed() && arg0.getValueIsAdjusting()) { if (table.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION && - singleSelectionListener != null) + singleSelectionListener != null) { singleSelectionListener.columnSelectionChanged(table.getSelectedColumn()); - else if (table.getSelectionModel().getSelectionMode() == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION && - multipleSelectionListener != null) + } else if (table.getSelectionModel().getSelectionMode() == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION && + multipleSelectionListener != null) { multipleSelectionListener.columnSelectionChanged(table.getSelectedColumns()); + } } } } - + private class RowSelectionListener implements ListSelectionListener { @Override - public void valueChanged(ListSelectionEvent arg0) { + public void valueChanged(final ListSelectionEvent arg0) { if (table.getRowSelectionAllowed() && arg0.getValueIsAdjusting()) { if (table.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION && - singleSelectionListener != null) + singleSelectionListener != null) { singleSelectionListener.rowSelectionChanged(table.getSelectedRow()); - else if (table.getSelectionModel().getSelectionMode() == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION && - multipleSelectionListener != null) + } else if (table.getSelectionModel().getSelectionMode() == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION && + multipleSelectionListener != null) { multipleSelectionListener.rowSelectionChanged(table.getSelectedRows()); + } } } } - + public interface SingleSelectionListener { public void columnSelectionChanged(int selectedColumn); public void rowSelectionChanged(int selectedRow); } - + public interface MultipleSelectionListener { public void columnSelectionChanged(int[] selectedColumns); @@ -434,26 +445,26 @@ public int getFirstLineWithData() { /** * @param firstLineWithData the firstLineWithData to set */ - public void setFirstLineWithData(int firstLineWithData) { + public void setFirstLineWithData(final int firstLineWithData) { TableController.firstLineWithData = firstLineWithData; } - + public String[] getAllColumnHeadings(){ - return this.getColumnHeadingsFiltered(null,false); + return getColumnHeadingsFiltered(null,false); } - + public String[] getUsedColumnHeadings() { - return this.getColumnHeadingsFiltered(Lang.l().step3ColTypeDoNotExport(),false); - } - - public String[] getColumnHeadingsFiltered(String typeToLeaveOut, boolean withColId) { - int colCount = this.table.getColumnCount(); - TableColumnModel tcm = this.table.getColumnModel(); - // Check for null and empty strings - boolean filter = (typeToLeaveOut == null? false: + return getColumnHeadingsFiltered(Lang.l().step3ColTypeDoNotExport(),false); + } + + public String[] getColumnHeadingsFiltered(final String typeToLeaveOut, final boolean withColId) { + final int colCount = table.getColumnCount(); + final TableColumnModel tcm = table.getColumnModel(); + // Check for null and empty strings + final boolean filter = (typeToLeaveOut == null? false: typeToLeaveOut.equalsIgnoreCase("")? false : true ); - ArrayList headings = new ArrayList(colCount); + final ArrayList headings = new ArrayList(colCount); if (!filter) { for (int i = 0; i < colCount; i++) { String tmp = tcm.getColumn(i).getHeaderValue().toString(); @@ -473,11 +484,11 @@ public String[] getColumnHeadingsFiltered(String typeToLeaveOut, boolean withCol } } } - headings.trimToSize(); + headings.trimToSize(); return headings.toArray(new String[headings.size()]); } public String[] getUsedColumnHeadingsWithId() { - return this.getColumnHeadingsFiltered(Lang.l().step3ColTypeDoNotExport(),true); + return getColumnHeadingsFiltered(Lang.l().step3ColTypeDoNotExport(),true); } }