1 package org.simantics.district.network.ui.techtype.table;
3 import java.io.IOException;
4 import java.io.StringReader;
5 import java.nio.file.Path;
6 import java.nio.file.Paths;
7 import java.util.ArrayList;
8 import java.util.Comparator;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.stream.IntStream;
13 import org.apache.commons.csv.CSVRecord;
14 import org.eclipse.core.runtime.ListenerList;
15 import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
16 import org.eclipse.nebula.widgets.nattable.sort.SortDirectionEnum;
17 import org.simantics.district.imports.DistrictImportUtils;
18 import org.simantics.district.network.techtype.TechTypeUtils;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
22 public class TechTypeTableDataProvider implements IDataProvider {
24 private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableDataProvider.class);
26 private List<CSVRecord> records = new ArrayList<>();
27 private boolean[] enabled;
28 private String filter = null;
29 private List<String> variables = null;
30 private List<String> headers = null;
31 private int[] filteredRows;
33 private ListenerList<EnableListener> enableListeners = new ListenerList<EnableListener>();
35 private int[] sortedRows;
37 private boolean showEnabled = true;
39 private static final Comparator<? super String> VALUE_COMPARATOR = (a, b) -> {
41 double da = Double.valueOf(a.replace(",", "."));
42 double db = Double.valueOf(b.replace(",", "."));
43 return Double.compare(da, db);
44 } catch (NumberFormatException e) {
45 return TechTypeUtils.compareNatural(a, b);
49 public TechTypeTableDataProvider(String data, int[] enabledList) {
51 setEnabledFlags(enabledList);
52 showEnabled = enabledList != null;
55 public TechTypeTableDataProvider(String data) {
60 public void setEnabledFlags(int[] enabledList) {
61 this.enabled = new boolean[records.size()];
62 if (enabledList != null) {
63 for (int i : enabledList) {
64 if (i >= 0 && i < enabled.length)
69 showEnabled = enabledList != null;
72 public boolean isCheckBoxColumn(int columnIndex) {
73 return isEnabledColumn(columnIndex);
76 public String getVariableName(int columnIndex) {
77 return variables != null && columnIndex > 0 && columnIndex <= variables.size()
78 ? variables.get(columnIndex - columnOffset())
82 private int columnOffset() {
83 return showEnabled ? 1 : 0;
86 public int getVariableIndex(String variableName) {
87 return variables != null ? variables.indexOf(variableName) + columnOffset() : -1;
90 public CSVRecord getRecord(int rowIndex) {
91 return records.get(recordIndex(rowIndex));
94 public boolean isEnabled(int rowIndex) {
95 return enabled[recordIndex(rowIndex)];
98 private int recordIndex(int rowIndex) {
99 return sortedRows[filteredRows[rowIndex]];
102 public String getHeaderValue(int columnIndex) {
103 if (headers == null) {
105 } else if (isEnabledColumn(columnIndex)) {
108 return headers.get(columnIndex - columnOffset());
113 public Object getDataValue(int columnIndex, int rowIndex) {
114 if (isEnabledColumn(columnIndex)) {
115 return isEnabled(rowIndex);
117 return getRecord(rowIndex).get(columnIndex - columnOffset());
121 public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
122 if (isEnabledColumn(columnIndex)) {
123 boolean value = Boolean.parseBoolean((String) newValue);
124 enabled[recordIndex(rowIndex)] = value;
125 fireEnableEvent(rowIndex, value);
129 public void addEnableListener(EnableListener listener) {
130 enableListeners.add(listener);
133 private void fireEnableEvent(int rowIndex, boolean newValue) {
134 enableListeners.forEach(l -> l.rowEnabled(rowIndex, newValue));
138 public int getColumnCount() {
139 if (records.isEmpty()) {
142 return records.get(0).size() + columnOffset();
146 public int getRowCount() {
147 return filteredRows.length;
150 public boolean isEditable(int columnIndex, int rowIndex) {
151 return isEnabledColumn(columnIndex);
154 private boolean isEnabledColumn(int columnIndex) {
155 return showEnabled && columnIndex == 0;
158 public void setFilter(String text) {
159 this.filter = text != null ? text.toLowerCase() : null;
161 filteredRows = IntStream.range(0, records.size())
162 .filter(k -> isMatch(records.get(sortedRows[k]), filter))
166 private static boolean isMatch(CSVRecord record, String filterString) {
167 if (filterString == null || filterString.isEmpty())
170 for (int i = 0; i < record.size(); i++) {
171 String columnContent = record.get(i);
172 if (columnContent.toLowerCase().contains(filterString)) {
181 * Read a CSV file into table contents.
183 * Set path to null to create an empty table.
185 * @param path The path of the CSV file to be read.
187 public void setPath(String path) {
191 Path techTypeCsv = Paths.get(path);
193 DistrictImportUtils.consumeCSV(techTypeCsv, ';', false, record -> {
197 } catch (IOException e) {
202 enabled = new boolean[records.size()];
203 sortedRows = IntStream.range(0, records.size()).toArray();
209 * Set table data contents to a given string of CSV data.
211 * Set 'data' to null to create an empty table.
213 * @param data The CSV data to be shown in the table.
215 public void setData(String data) {
219 long ncommas = data.chars().filter(c -> c == ',').count();
220 long nsemis = data.chars().filter(c -> c == ';').count();
221 char delim = nsemis > ncommas ? ';' : ',';
222 StringReader reader = new StringReader(data);
224 DistrictImportUtils.consumeCSV(reader, delim, false, record -> {
228 } catch (IOException e) {
229 LOGGER.error("Error reading CSV file", e);
233 CSVRecord header = records.remove(0);
234 CSVRecord units = records.remove(0);
236 variables = new ArrayList<>();
237 headers = new ArrayList<>();
239 Iterator<String> it = header.iterator();
240 Iterator<String> uit = units.iterator();
242 while (it.hasNext()) {
243 String variable = it.next().trim();
244 String unit = uit.hasNext() ? uit.next().trim() : null;
246 variables.add(variable);
247 headers.add(variable + (unit != null && !unit.isEmpty() && !(unit.startsWith("(") && unit.endsWith(")")) ? " [" + unit + "]" : ""));
251 enabled = new boolean[records.size()];
252 sortedRows = IntStream.range(0, records.size()).toArray();
257 public void sortBy(int columnIndex, SortDirectionEnum sortDirection) {
259 if (columnIndex >= 0 && !sortDirection.equals(SortDirectionEnum.NONE)) {
260 int offset = columnOffset();
261 Comparator<Integer> comparator = isEnabledColumn(columnIndex) ?
262 Comparator.comparing(k -> enabled[sortedRows[(int) k]]) :
263 Comparator.comparing(k -> records.get(sortedRows[(int) k]).get(columnIndex-offset), VALUE_COMPARATOR);
265 if (sortDirection.equals(SortDirectionEnum.DESC))
266 comparator = comparator.reversed();
268 sortedRows = IntStream.range(0, records.size())
271 .mapToInt(i -> sortedRows[i])
274 sortedRows = IntStream.range(0, records.size()).toArray();