package org.simantics.district.network.ui.techtype.table; import java.io.IOException; import java.io.StringReader; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.stream.IntStream; import org.apache.commons.csv.CSVRecord; import org.eclipse.core.runtime.ListenerList; import org.eclipse.nebula.widgets.nattable.data.IDataProvider; import org.eclipse.nebula.widgets.nattable.sort.SortDirectionEnum; import org.simantics.district.imports.DistrictImportUtils; import org.simantics.district.network.techtype.TechTypeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TechTypeTableDataProvider implements IDataProvider { private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableDataProvider.class); private List records = new ArrayList<>(); private boolean[] enabled; private String filter = null; private List variables = null; private List headers = null; private int[] filteredRows; private ListenerList enableListeners = new ListenerList(); private int[] sortedRows; private boolean showEnabled = true; private static final Comparator VALUE_COMPARATOR = (a, b) -> { try { double da = Double.valueOf(a.replace(",", ".")); double db = Double.valueOf(b.replace(",", ".")); return Double.compare(da, db); } catch (NumberFormatException e) { return TechTypeUtils.compareNatural(a, b); } }; public TechTypeTableDataProvider(String data, int[] enabledList) { setData(data); setEnabledFlags(enabledList); showEnabled = enabledList != null; } public TechTypeTableDataProvider(String data) { setData(data); showEnabled = false; } public void setEnabledFlags(int[] enabledList) { this.enabled = new boolean[records.size()]; if (enabledList != null) { for (int i : enabledList) { if (i >= 0 && i < enabled.length) enabled[i] = true; } } showEnabled = enabledList != null; } public boolean isCheckBoxColumn(int columnIndex) { return isEnabledColumn(columnIndex); } public String getVariableName(int columnIndex) { return variables != null && columnIndex > 0 && columnIndex <= variables.size() ? variables.get(columnIndex - columnOffset()) : null; } private int columnOffset() { return showEnabled ? 1 : 0; } public int getVariableIndex(String variableName) { return variables != null ? variables.indexOf(variableName) + columnOffset() : -1; } public CSVRecord getRecord(int rowIndex) { return records.get(recordIndex(rowIndex)); } public boolean isEnabled(int rowIndex) { return enabled[recordIndex(rowIndex)]; } private int recordIndex(int rowIndex) { return sortedRows[filteredRows[rowIndex]]; } public String getHeaderValue(int columnIndex) { if (headers == null) { return ""; } else if (isEnabledColumn(columnIndex)) { return "Enabled"; } else { return headers.get(columnIndex - columnOffset()); } } @Override public Object getDataValue(int columnIndex, int rowIndex) { if (isEnabledColumn(columnIndex)) { return isEnabled(rowIndex); } return getRecord(rowIndex).get(columnIndex - columnOffset()); } @Override public void setDataValue(int columnIndex, int rowIndex, Object newValue) { if (isEnabledColumn(columnIndex)) { boolean value = Boolean.parseBoolean((String) newValue); enabled[recordIndex(rowIndex)] = value; fireEnableEvent(rowIndex, value); } } public void addEnableListener(EnableListener listener) { enableListeners.add(listener); } private void fireEnableEvent(int rowIndex, boolean newValue) { enableListeners.forEach(l -> l.rowEnabled(rowIndex, newValue)); } @Override public int getColumnCount() { if (records.isEmpty()) { return 0; } return records.get(0).size() + columnOffset(); } @Override public int getRowCount() { return filteredRows.length; } public boolean isEditable(int columnIndex, int rowIndex) { return isEnabledColumn(columnIndex); } private boolean isEnabledColumn(int columnIndex) { return showEnabled && columnIndex == 0; } public void setFilter(String text) { this.filter = text != null ? text.toLowerCase() : null; filteredRows = IntStream.range(0, records.size()) .filter(k -> isMatch(records.get(sortedRows[k]), filter)) .toArray(); } private static boolean isMatch(CSVRecord record, String filterString) { if (filterString == null || filterString.isEmpty()) return true; for (int i = 0; i < record.size(); i++) { String columnContent = record.get(i); if (columnContent.toLowerCase().contains(filterString)) { return true; } } return false; } /** * Read a CSV file into table contents. * * Set path to null to create an empty table. * * @param path The path of the CSV file to be read. */ public void setPath(String path) { records.clear(); if (path != null) { Path techTypeCsv = Paths.get(path); try { DistrictImportUtils.consumeCSV(techTypeCsv, ';', false, record -> { records.add(record); return true; }); } catch (IOException e) { e.printStackTrace(); } } enabled = new boolean[records.size()]; sortedRows = IntStream.range(0, records.size()).toArray(); setFilter(null); } /** * Set table data contents to a given string of CSV data. * * Set 'data' to null to create an empty table. * * @param data The CSV data to be shown in the table. */ public void setData(String data) { records.clear(); if (data != null) { long ncommas = data.chars().filter(c -> c == ',').count(); long nsemis = data.chars().filter(c -> c == ';').count(); char delim = nsemis > ncommas ? ';' : ','; StringReader reader = new StringReader(data); try { DistrictImportUtils.consumeCSV(reader, delim, false, record -> { records.add(record); return true; }); } catch (IOException e) { LOGGER.error("Error reading CSV file", e); return; } CSVRecord header = records.remove(0); CSVRecord units = records.remove(0); variables = new ArrayList<>(); headers = new ArrayList<>(); Iterator it = header.iterator(); Iterator uit = units.iterator(); while (it.hasNext()) { String variable = it.next().trim(); String unit = uit.hasNext() ? uit.next().trim() : null; variables.add(variable); headers.add(variable + (unit != null && !unit.isEmpty() && !(unit.startsWith("(") && unit.endsWith(")")) ? " [" + unit + "]" : "")); } } enabled = new boolean[records.size()]; sortedRows = IntStream.range(0, records.size()).toArray(); setFilter(null); } public void sortBy(int columnIndex, SortDirectionEnum sortDirection) { if (columnIndex >= 0 && !sortDirection.equals(SortDirectionEnum.NONE)) { int offset = columnOffset(); Comparator comparator = isEnabledColumn(columnIndex) ? Comparator.comparing(k -> enabled[sortedRows[(int) k]]) : Comparator.comparing(k -> records.get(sortedRows[(int) k]).get(columnIndex-offset), VALUE_COMPARATOR); if (sortDirection.equals(SortDirectionEnum.DESC)) comparator = comparator.reversed(); sortedRows = IntStream.range(0, records.size()) .mapToObj(i -> i) .sorted(comparator) .mapToInt(i -> sortedRows[i]) .toArray(); } else { sortedRows = IntStream.range(0, records.size()).toArray(); } } }