]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableDataProvider.java
af0e28d07bc84be4b9b01ab221105b2f3c5269a9
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / techtype / table / TechTypeTableDataProvider.java
1 package org.simantics.district.network.ui.techtype.table;
2
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;
12
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;
21
22 public class TechTypeTableDataProvider implements IDataProvider {
23
24     private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableDataProvider.class);
25
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;
32
33     private ListenerList<EnableListener> enableListeners = new ListenerList<EnableListener>();
34
35     private int[] sortedRows;
36
37         private static final Comparator<? super String> VALUE_COMPARATOR = (a, b) -> {
38                 try {
39                         double da = Double.valueOf(a.replace(",", "."));
40                         double db = Double.valueOf(b.replace(",", "."));
41                         return Double.compare(da, db);
42                 } catch (NumberFormatException e) {
43                         return TechTypeUtils.compareNatural(a, b);
44                 }
45         };
46
47     public TechTypeTableDataProvider(String data, int[] enabledList) {
48         setData(data);
49         setEnabledFlags(enabledList);
50     }
51     
52     public TechTypeTableDataProvider(String data) {
53         // load csv
54         setData(data);
55     }
56     
57     public void setEnabledFlags(int[] enabledList) {
58         this.enabled = new boolean[records.size()];
59         if (enabledList != null) {
60             for (int i : enabledList) {
61                 if (i >= 0 && i < enabled.length)
62                     enabled[i] = true;
63             }
64         }
65     }
66
67     public String getVariableName(int columnIndex) {
68         return variables != null && columnIndex > 0 && columnIndex <= variables.size() ? variables.get(columnIndex - 1) : null;
69     }
70
71     public int getVariableIndex(String variableName) {
72         return variables != null ? variables.indexOf(variableName) + 1 : -1;
73     }
74     
75     public CSVRecord getRecord(int rowIndex) {
76         return records.get(recordIndex(rowIndex));
77     }
78
79     public boolean isEnabled(int rowIndex) {
80         return enabled[recordIndex(rowIndex)];
81     }
82
83     private int recordIndex(int rowIndex) {
84         return sortedRows[filteredRows[rowIndex]];
85     }
86     
87     public String getHeaderValue(int columnIndex) {
88         if (headers == null) {
89             return "<empty>";
90         } else if (columnIndex == 0) {
91             return "Enabled";
92         } else {
93             return headers.get(columnIndex - 1);
94         }
95     }
96
97     @Override
98     public Object getDataValue(int columnIndex, int rowIndex) {
99         if (columnIndex == 0) {
100             return isEnabled(rowIndex);
101         }
102         return getRecord(rowIndex).get(columnIndex - 1);
103     }
104
105     @Override
106     public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
107         if (columnIndex == 0) {
108             boolean value = Boolean.parseBoolean((String) newValue);
109             enabled[recordIndex(rowIndex)] = value;
110             fireEnableEvent(rowIndex, value);
111         }
112     }
113     
114     public void addEnableListener(EnableListener listener) {
115         enableListeners.add(listener);
116     }
117
118     private void fireEnableEvent(int rowIndex, boolean newValue) {
119         enableListeners.forEach(l -> l.rowEnabled(rowIndex, newValue));
120     }
121
122     @Override
123     public int getColumnCount() {
124         if (records.isEmpty()) {
125             return 0;
126         }
127         return records.get(0).size() + 1;
128     }
129
130     @Override
131     public int getRowCount() {
132         return filteredRows.length;
133     }
134
135     public boolean isEditable(int columnIndex, int rowIndex) {
136         return columnIndex == 0;
137     }
138
139     public void setFilter(String text) {
140         this.filter = text != null ? text.toLowerCase() : null;
141
142         filteredRows = IntStream.range(0, records.size())
143                 .filter(k -> isMatch(records.get(sortedRows[k]), filter))
144                 .toArray();
145     }
146
147     private static boolean isMatch(CSVRecord record, String filterString) {
148         if (filterString == null || filterString.isEmpty())
149             return true;
150         
151         for (int i = 0; i < record.size(); i++) {
152             String columnContent = record.get(i);
153             if (columnContent.toLowerCase().contains(filterString)) {
154                 return true;
155             }
156         }
157         
158         return false;
159     }
160
161     /**
162      * Read a CSV file into table contents.
163      * 
164      * Set path to null to create an empty table.
165      * 
166      * @param path  The path of the CSV file to be read.
167      */
168     public void setPath(String path) {
169         records.clear();
170
171         if (path != null) {
172             Path techTypeCsv = Paths.get(path);
173             try {
174                 DistrictImportUtils.consumeCSV(techTypeCsv, ';', false, record -> {
175                     records.add(record);
176                     return true;
177                 });
178             } catch (IOException e) {
179                 e.printStackTrace();
180             }
181         }
182
183         enabled = new boolean[records.size()];
184         sortedRows = IntStream.range(0, records.size()).toArray();
185         
186         setFilter(null);
187     }
188
189     /**
190      * Set table data contents to a given string of CSV data.
191      * 
192      * Set 'data' to null to create an empty table.
193      * 
194      * @param data  The CSV data to be shown in the table.
195      */
196     public void setData(String data) {
197         records.clear();
198
199         if (data != null) {
200             long ncommas = data.chars().filter(c -> c == ',').count();
201             long nsemis = data.chars().filter(c -> c == ';').count();
202             char delim = nsemis > ncommas ? ';' : ',';
203             StringReader reader = new StringReader(data);
204             try {
205                 DistrictImportUtils.consumeCSV(reader, delim, false, record -> {
206                     records.add(record);
207                     return true;
208                 });
209             } catch (IOException e) {
210                 LOGGER.error("Error reading CSV file", e);
211                 return;
212             }
213
214             CSVRecord header = records.remove(0);
215             CSVRecord units = records.remove(0);
216
217             variables = new ArrayList<>();
218             headers = new ArrayList<>();
219
220             Iterator<String> it = header.iterator();
221             Iterator<String> uit = units.iterator();
222
223             while (it.hasNext()) {
224                 String variable = it.next().trim();
225                 String unit = uit.hasNext() ? uit.next().trim() : null;
226
227                 variables.add(variable);
228                 headers.add(variable + (unit != null && !unit.isEmpty() && !(unit.startsWith("(") && unit.endsWith(")")) ? " [" + unit + "]" : ""));
229             }
230         }
231         
232         enabled = new boolean[records.size()];
233         sortedRows = IntStream.range(0, records.size()).toArray();
234
235         setFilter(null);
236     }
237
238     public void sortBy(int columnIndex, SortDirectionEnum sortDirection) {
239         
240         if (columnIndex >= 0 && !sortDirection.equals(SortDirectionEnum.NONE)) {
241             Comparator<Integer> comparator = columnIndex == 0 ?
242                     Comparator.comparing(k -> enabled[sortedRows[(int) k]]) :
243                     Comparator.comparing(k -> records.get(sortedRows[(int) k]).get(columnIndex-1), VALUE_COMPARATOR);
244             
245             if (sortDirection.equals(SortDirectionEnum.DESC))
246                 comparator = comparator.reversed();
247             
248             sortedRows = IntStream.range(0, records.size())
249                     .mapToObj(i -> i)
250                     .sorted(comparator)
251                     .mapToInt(i -> sortedRows[i])
252                     .toArray();
253         } else {
254             sortedRows = IntStream.range(0, records.size()).toArray();
255         }
256     }
257
258 }