Projekt

Obecné

Profil

Stáhnout (37.3 KB) Statistiky
| Větev: | Revize:
1
package vldc.aswi.utils;
2

    
3
import vldc.aswi.model.table.*;
4
import vldc.aswi.model.table.contingencyTable.ContingencyTableRow;
5
import vldc.aswi.model.table.contingencyTable.ContingencyTableRowCell;
6

    
7
import java.text.DecimalFormat;
8
import java.util.ArrayList;
9
import java.util.List;
10
import java.util.Map;
11
import java.util.Stack;
12
import java.util.concurrent.TimeUnit;
13

    
14
/**
15
 * Class used to convert original table to contingency table.
16
 */
17
public class Converter {
18

    
19
    /**
20
     * Name of the table
21
     */
22
    private final String tableName;
23

    
24
    /**
25
     * Stores information about whether user set switch to show empty rows and columns to ON.
26
     */
27
    private final boolean isNotRemoveEmpty;
28

    
29
    /**
30
     * Map of original table.
31
     */
32
    private final Map<String, TableColumn> tableColumns;
33

    
34
    /**
35
     * List of names and user names of selected rows.
36
     */
37
    private final List<NameUserName> rowNames;
38

    
39
    /**
40
     * List of names and user names of selected columns.
41
     */
42
    private final List<NameUserName> columnNames;
43

    
44
    /**
45
     * List of information about selected value and functions.
46
     */
47
    private final List<ValueFunction> valueFunctions;
48

    
49
    /**
50
     * List of columns node of contingency table going from left to right.
51
     */
52
    private List<Node> columnNodes;
53

    
54
    /**
55
     * Formatter of the cell content.
56
     */
57
    private final DecimalFormat decimalFormat = new DecimalFormat("0.###");
58

    
59
    /**
60
     * Creates and initializes converter.
61
     * @param tableColumns Map of original table.
62
     * @param rowNames List of names and user names of selected rows.
63
     * @param columnNames List of names and user names of selected columns.
64
     * @param valueFunctions List of information about selected value and functions.
65
     * @param isNotRemoveEmpty Stores information about whether user set switch to show empty rows and columns to ON.
66
     * @param tableName Name of the table.
67
     */
68
    public Converter(Map<String, TableColumn> tableColumns, List<NameUserName> rowNames, List<NameUserName> columnNames,
69
                     List<ValueFunction> valueFunctions, boolean isNotRemoveEmpty, String tableName) {
70
        this.tableColumns = tableColumns;
71
        this.rowNames = rowNames;
72
        this.columnNames = columnNames;
73
        this.valueFunctions = valueFunctions;
74
        this.isNotRemoveEmpty = isNotRemoveEmpty;
75
        this.tableName = tableName;
76
    }
77

    
78
    /**
79
     * Convert Map of TableColumns into list of ContingencyTableRow.
80
     * @return List of ContingencyTableRow.
81
     */
82
    public List<ContingencyTableRow> convertTableColumnsToContingencyTableRows() {
83
        long startTime = System.nanoTime();
84

    
85
        long startTime1 = System.nanoTime();
86
        columnNodes = new ArrayList<>();
87
        List<List<String>> values = new ArrayList<>();
88
        for(NameUserName nameUserName : columnNames) {
89
            TableColumn column = tableColumns.get(nameUserName.getName());
90
            values.add(getOriginalValuesFromList(column.getValues()));
91
        }
92

    
93
        Node startNode = new Node();
94
        generateColumns(0, values, columnNodes, startNode);
95

    
96
        long stopTime1 = System.nanoTime();
97
        System.out.println("Colums list:" + TimeUnit.MILLISECONDS.convert((stopTime1 - startTime1), TimeUnit.NANOSECONDS));
98

    
99
        List<ContingencyTableRow> contingencyTableRows = new ArrayList<>();
100

    
101
        long startTime2 = System.nanoTime();
102
        List<ContingencyTableRow> dataRows = createDataRows();
103
        long stopTime2 = System.nanoTime();
104
        System.out.println("Rows:" + TimeUnit.MILLISECONDS.convert((stopTime2 - startTime2), TimeUnit.NANOSECONDS));
105

    
106
        if (isNotRemoveEmpty) {
107
            long startTime3 = System.nanoTime();
108
            for (ContingencyTableRow dataRow : dataRows) {
109
                for (int i = 0; i < columnNodes.size(); i++) {
110
                    if (!columnNodes.get(i).isUsable()) {
111
                        dataRow.getCells().remove(i + 1);
112
                    }
113
                }
114
            }
115

    
116
            columnNodes.removeIf(node -> !node.isUsable());
117
            long stopTime3 = System.nanoTime();
118
            System.out.println("Columns remove:" + TimeUnit.MILLISECONDS.convert((stopTime3 - startTime3), TimeUnit.NANOSECONDS));
119
        }
120

    
121
        long startTime4 = System.nanoTime();
122
        List<ContingencyTableRow> headerRows = createHeaderOnlyRows();
123
        long stopTime4 = System.nanoTime();
124
        System.out.println("Headers:" + TimeUnit.MILLISECONDS.convert((stopTime4 - startTime4), TimeUnit.NANOSECONDS));
125

    
126
        contingencyTableRows.addAll(headerRows);
127
        contingencyTableRows.addAll(dataRows);
128

    
129
        long stopTime = System.nanoTime();
130
        System.out.println("Converter:" + TimeUnit.SECONDS.convert((stopTime - startTime), TimeUnit.NANOSECONDS) + "s");
131

    
132
        return contingencyTableRows;
133
    }
134

    
135
    /**
136
     * Generates header rows.
137
     * @return list of contingency table rows
138
     */
139
    private List<ContingencyTableRow> createHeaderOnlyRows() {
140
        List<ContingencyTableRow> headerRows = new ArrayList<>();
141

    
142
        headerRows.add(new ContingencyTableRow(true, 0));
143

    
144
        for (int i = 0; i < columnNames.size(); i++) {
145
            headerRows.add(new ContingencyTableRow(true, 0));
146
        }
147

    
148
        if (valueFunctions.get(0).getFunction() != null && valueFunctions.get(0).getFunction().equals("NIC") &&
149
                columnNames.size() == 0) {
150
            headerRows.get(0).addTableRowCell(new ContingencyTableRowCell("Seznam", 1));
151

    
152
            return headerRows;
153
        }
154

    
155
        for (int i = 0; i < headerRows.size(); i++) {
156
            ContingencyTableRow headerRow = headerRows.get(i);
157

    
158
            if (i == 0) {
159
                headerRow.addTableRowCell(new ContingencyTableRowCell(tableName, 1));
160
            }
161
            else {
162
                headerRow.addTableRowCell(new ContingencyTableRowCell("", 1));
163
            }
164
        }
165

    
166
        List<String> prevValues = new ArrayList<>(); // values that were visited
167
        int prevSize = 0; // number of previous column node values
168
        for (ValueFunction valueFunction : valueFunctions) { // for each function
169
            // add cell with the function name and value name to the 0th row with span 0
170
            headerRows.get(0).addTableRowCell(new ContingencyTableRowCell(valueFunction.getFunction() + " z " + valueFunction.getParameterName(), 0));
171
            for (Node columnNode : columnNodes) { // for each column
172
                for (int i = 0; i < columnNode.getValues().size(); i++) { // for each value in column
173
                    String value = columnNode.getValues().get(i);
174
                    // check if value is not in the visited list, if list size equals i, the value is not visited
175
                    // eg. i = 0 -> no values in the list, if list size is bigger than i, there are more values than i
176
                    // so then check if value at ith position is not equal to the current value (is not visited)
177
                    if (prevValues.size() == i || (prevValues.size() > i && !prevValues.get(i).equals(value))) {
178
                        prevValues.add(value);
179
                        // add cell with value with span 0 to the i + 1th row (0th is for function name and value name cells)
180
                        headerRows.get(i + 1).addTableRowCell(new ContingencyTableRowCell(columnNames.get(i).getUserName() + " - " + value, 1));
181
                        // add 1 to the span of the last added function and value name cell
182
                        headerRows.get(0).getCells().get(headerRows.get(0).getCells().size() - 1).setColSpan(headerRows.get(0).getCells().get(headerRows.get(0).getCells().size() - 1).getColSpan() + 1);
183
                    } else if (columnNode.getValues().size() >= prevSize) {
184
                        // add 1 to the span of the last added cell of the row of i + 1th value
185
                        headerRows.get(i + 1).getCells().get(headerRows.get(i + 1).getCells().size() - 1).setColSpan(headerRows.get(i + 1).getCells().get(headerRows.get(i + 1).getCells().size() - 1).getColSpan() + 1);
186
                    }
187
                }
188

    
189
                if (columnNode.getValues().size() < prevSize) { // if there are less values than in previous column node
190
                    for (int s = prevSize; s < headerRows.size(); s++) {
191
                        // add blank cell to the rows bellow
192
                        headerRows.get(s).addTableRowCell(new ContingencyTableRowCell("", 1));
193
                    }
194
                    for (int s = 1; s < columnNode.getValues().size(); s++) {
195
                        // add 1 to colspan to the cells above
196
                        headerRows.get(s).getCells().get(headerRows.get(s).getCells().size() - 1).setColSpan(headerRows.get(s).getCells().get(headerRows.get(s).getCells().size() - 1).getColSpan() + 1);
197
                    }
198
                    // create cell for total values (not for lists of the tree)
199
                    headerRows.get(columnNode.getValues().size()).addTableRowCell(new ContingencyTableRowCell(columnNode.getValues().get(columnNode.getValues().size() - 1) + " Celkem", 1));
200
                }
201

    
202
                prevSize = columnNode.getValues().size();
203

    
204
                if (!prevValues.isEmpty()) {
205
                    prevValues.remove(prevValues.size() - 1); // remove last value
206
                }
207
            }
208
        }
209

    
210
        for (ValueFunction valueFunction : valueFunctions) {
211
            for (int i = 1; i < headerRows.size(); i++) {
212
                headerRows.get(i).addTableRowCell(new ContingencyTableRowCell("", 1));
213
            }
214
            headerRows.get(0).addTableRowCell(new ContingencyTableRowCell("Celkový " + valueFunction.getFunction() +
215
                    " z " + valueFunction.getParameterName(), 1));
216
        }
217

    
218
        return headerRows;
219
    }
220

    
221
    /**
222
     * Gets original values of a given list.
223
     * @param list List from which original values should be taken.
224
     * @return List of original values.
225
     */
226
    private List<String> getOriginalValuesFromList(List<String> list) {
227
        List<String> origList = new ArrayList<>();
228

    
229
        for (String string : list) {
230
            if (origList.stream().noneMatch(s -> s.equals(string))) {
231
                origList.add(string);
232
            }
233
        }
234

    
235
        return origList;
236
    }
237

    
238
    /**
239
     * Creates data rows.
240
     * @return List of contingency table rows.
241
     */
242
    private List<ContingencyTableRow> createDataRows() {
243
        List<ContingencyTableRow> dataRows = new ArrayList<>();
244
        List<Node> rowNodes = new ArrayList<>();
245
        List<List<String>> values = new ArrayList<>();
246
        for(NameUserName nameUserName : rowNames) {
247
            TableColumn column = tableColumns.get(nameUserName.getName());
248
            values.add(getOriginalValuesFromList(column.getValues()));
249
        }
250

    
251
        long startTime = System.nanoTime();
252
        Node node = new Node();
253
        generateRows(dataRows, 0, values, rowNodes, node);
254
        long stopTime = System.nanoTime();
255
        System.out.println("Rows List:" + TimeUnit.MILLISECONDS.convert((stopTime - startTime), TimeUnit.NANOSECONDS));
256

    
257
        // loop through all generated rows and store unusable rows to the list rowsToBeRemoved
258
        List<ContingencyTableRow> rowsToBeRemoved = new ArrayList<>();
259
        for (int i = 0; i < dataRows.size(); i++) {
260
            if (!fillRowWithData(dataRows.get(i), rowNodes.get(i))) {
261
                rowsToBeRemoved.add(dataRows.get(i));
262
            }
263
        }
264

    
265
        // remove unusable rows
266
        dataRows.removeAll(rowsToBeRemoved);
267

    
268
        // create final row (row with total values of each column)
269
        if (valueFunctions.get(0).getFunction() != null && (!valueFunctions.get(0).getFunction().equals("NIC") ||
270
                (valueFunctions.get(0).getFunction().equals("NIC") && columnNames.size() > 0))) {
271
            dataRows.add(createFinalRow());
272
        }
273

    
274
        return dataRows;
275
    }
276

    
277
    /**
278
     * Recursively generates all possible row combinations, sorted as in contingency table.
279
     * @param rows List of sorted contingency table rows.
280
     * @param index Index in the values list.
281
     * @param values List of lists of values of each query row.
282
     * @param rowNodes List of row nodes.
283
     * @param prevNode Parent node of the nodes.
284
     */
285
    private void generateRows(List<ContingencyTableRow> rows, int index, List<List<String>> values, List<Node> rowNodes,
286
                              Node prevNode) {
287
        if (index < values.size()) {
288
            List<String> list = values.get(index);
289
            for (String s : list) {
290
                StringBuilder levelString = new StringBuilder();
291
                // add indent if row value has index higher than 0
292
                for (int i = 0; i < index; i++) {
293
                    levelString.append("      ");
294
                }
295
                levelString.append(rowNames.get(index).getUserName()).append(" - ").append(s);
296
                ContingencyTableRow row = new ContingencyTableRow(false, index);
297
                row.addTableRowCell(new ContingencyTableRowCell(levelString.toString(), 1));
298
                rows.add(row);
299
                Node node = new Node(prevNode);
300
                // save a value from a chosen row
301
                node.getValues().add(s);
302
                // visit (save) root
303
                rowNodes.add(node);
304
                int newIndex = index + 1;
305
                // visit sub tree
306
                generateRows(rows, newIndex, values, rowNodes, node);
307
            }
308
        }
309
    }
310

    
311
    /**
312
     * Recursively generates all possible column combinations, sorted as in contingency table.
313
     * @param index Index in the values list.
314
     * @param values List of lists of values of each query row.
315
     * @param columnNodes List of column nodes.
316
     * @param prevNode Parent node of the nodes.
317
     */
318
    private void generateColumns(int index, List<List<String>> values, List<Node> columnNodes, Node prevNode) {
319
        if (index < values.size()) {
320
            List<String> list = values.get(index);
321
            for (String s : list) {
322
                Node node = new Node(prevNode);
323
                // save a value from a chosen column
324
                node.getValues().add(s);
325
                if (isNotRemoveEmpty) {
326
                    int newIndex = index + 1;
327
                    // first visit all subtrees
328
                    generateColumns(newIndex, values, columnNodes, node);
329
                    // visit (save) root
330
                    columnNodes.add(node);
331
                }
332
                else if (checkIfColumnIsUsable(node.getValues())) {
333
                    int newIndex = index + 1;
334
                    // first visit all subtrees
335
                    generateColumns(newIndex, values, columnNodes, node);
336
                    // visit (save) root
337
                    columnNodes.add(node);
338
                }
339
            }
340
        }
341
    }
342

    
343
    /**
344
     * Fills each data row with data.
345
     * @param row Row to be filled with data.
346
     * @param rowNode Node of the row.
347
     */
348
    private boolean fillRowWithData(ContingencyTableRow row, Node rowNode) {
349
        if (valueFunctions.size() == 0) {
350
            return true;
351
        }
352

    
353
        boolean isSingleValueRow = false;
354

    
355
        // check if row is a root (single) value
356
        if (rowNode.getValues().size() <= 1) {
357
            isSingleValueRow = true;
358
        }
359

    
360
        // stores if row is usable
361
        boolean isRowUsable = false;
362

    
363
        // if user selected an option to show all rows and columns, set that row is always usable
364
        if (isNotRemoveEmpty) {
365
            isRowUsable = true;
366
        }
367

    
368
        // if no columns or functions were selected
369
        if (columnNodes.size() == 0) {
370
            columnNodes.add(new Node());
371
        }
372

    
373
        // load the list of usable original row IDs
374
        List<Integer> usableIDs = getUsableIDs(rowNode.getValues());
375

    
376
        if (valueFunctions.get(0).getFunction() != null && valueFunctions.get(0).getFunction().equals("NIC") &&
377
                !usableIDs.isEmpty()) { // return that row is usable because it is used for list generation
378
            return true;
379
        }
380
        else if (valueFunctions.get(0).getFunction() != null && valueFunctions.get(0).getFunction().equals("NIC") &&
381
                usableIDs.isEmpty()) { // no function selected and no usable rows in original return if row is usable
382
            return isRowUsable;
383
        }
384

    
385
        // array of total values for functions
386
        DoubleWrapper[] totals = new DoubleWrapper[valueFunctions.size()];
387
        initializeDoubleWrapperField(totals);
388

    
389
        // array of total sum values for functions if functions are AVG
390
        DoubleWrapper[] totalsAvgSum = new DoubleWrapper[valueFunctions.size()];
391
        initializeDoubleWrapperField(totalsAvgSum);
392

    
393
        // array that stores if total value cell will be filled or left blank for current row and each function
394
        boolean[] isFilledValueCells = new boolean[valueFunctions.size()];
395
        // for each function
396
        for (int v = 0; v < valueFunctions.size(); v++) {
397
            // if function has not been selected
398
            if (valueFunctions.get(v).getFunction() != null && (valueFunctions.get(v).getFunction().equals("") ||
399
                    valueFunctions.get(v).getFunction().equals("NIC"))) {
400
                isRowUsable = true;
401
                break;
402
            }
403

    
404
            // stores if first MIN or MAX total value was saved for current function
405
            BooleanWrapper isFirstMinMaxTotal = new BooleanWrapper(true);
406

    
407
            ValueFunction valueFunction = valueFunctions.get(v);
408
            // for each contingency table column
409
            for (Node columnNode : columnNodes) {
410
                // result that will be stored in a cell given by row and column rowNode
411
                DoubleWrapper result = new DoubleWrapper(0);
412
                // sum result used to calculate AVG result in a cell given by row and column rowNode
413
                DoubleWrapper resultAvgSum = new DoubleWrapper(0);
414
                // stores if first MIN or MAX value was saved for current cell
415
                BooleanWrapper isFirstMinMax = new BooleanWrapper(true);
416
                // stores if a cell will be filled or left blank
417
                boolean isFilledCell = false;
418
                // for each usable row
419
                for (Integer usableID : usableIDs) {
420
                    boolean isUsable = true;
421
                    // index of selected row
422
                    int j;
423
                    // check if values of the contingency table column rowNode are present in the row (at usableID position
424
                    // of original table) for columns that belong to column rowNode
425
                    for (j = 0; j < columnNode.getValues().size(); j++) {
426
                        if (!tableColumns.get(columnNames.get(j).getName()).getValues().get(usableID).equals(columnNode.getValues().get(j))) {
427
                            isUsable = false;
428
                            break;
429
                        }
430
                    }
431

    
432
                    // selected column from value
433
                    if (tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID) == null) {
434
                        isUsable = false;
435
                    }
436

    
437
                    // if value is usable, "intersect" of values from contingency table row and column, and value is true
438
                    if (isUsable) {
439
                        columnNode.setUsable(true);
440
                        isRowUsable = true;
441
                        isFilledCell = true;
442
                        computeValues(valueFunction, result, resultAvgSum, columnNode, isFirstMinMax,
443
                                usableID, false, isSingleValueRow);
444
                        // row is a root (single) value, save the result to the total function value cell
445
                        if (j <= 1) {
446
                            isFilledValueCells[v] = true;
447
                            computeValues(valueFunction, totals[v], totalsAvgSum[v], null, isFirstMinMaxTotal,
448
                                    usableID, true, isSingleValueRow);
449
                        }
450
                    }
451
                }
452
                // if cell should be blank, add empty value
453
                if (!isFilledCell) {
454
                    row.addTableRowCell(new ContingencyTableRowCell("", 0));
455
                }
456
                else { // generate a cell with a value
457
                    columnNode.setFilledTotalValue(true);
458
                    row.addTableRowCell(generateFilledCell(valueFunction.getFunction(), result.getValue(), result.getValue(), result.getValue(),
459
                            result.getValue(), resultAvgSum.getValue(), result.getValue()));
460
                }
461
            }
462
        }
463
        // generate cell for total function values
464
        for (int i = 0; i < valueFunctions.size(); i++) {
465
            if (valueFunctions.get(i).getFunction() != null && valueFunctions.get(i).getFunction().equals("NIC")) {
466
                break;
467
            }
468
            // if cell should be blank, add empty value
469
            if (!isFilledValueCells[i]) {
470
                row.addTableRowCell(new ContingencyTableRowCell("", 0));
471
            }
472
            else { // generate a cell with a value
473
                valueFunctions.get(i).setFilledTotalValue(true);
474
                row.addTableRowCell(generateFilledCell(valueFunctions.get(i).getFunction(), totals[i].getValue(), totals[i].getValue(),
475
                        totals[i].getValue(), totals[i].getValue(), totalsAvgSum[i].getValue(), totals[i].getValue()));
476
            }
477
        }
478

    
479
        // returns if row should be visible in the contingency table
480
        return isRowUsable;
481
    }
482

    
483
    /**
484
     * Gets a list of IDs of the query rows that fit the contingency table row settings.
485
     * @param rowNodeValues Values of a row node.
486
     * @return List of usable row IDs.
487
     */
488
    private List<Integer> getUsableIDs(List<String> rowNodeValues) {
489
        List<Integer> ids = new ArrayList<>();
490

    
491
        // for each row in original table
492
        for (int i = 0; i < tableColumns.get(rowNames.get(0).getName()).getValues().size(); i++) {
493
            boolean isUsable = true;
494
            // check if a row has values that are stored in the row node (for each chosen row)
495
            for (int j = 0; j < rowNodeValues.size(); j++) {
496
                if (!tableColumns.get(rowNames.get(j).getName()).getValues().get(i).equals(rowNodeValues.get(j))) {
497
                    isUsable = false;
498
                    break;
499
                }
500
            }
501
            if (isUsable) {
502
                ids.add(i);
503
            }
504
        }
505

    
506
        return ids;
507
    }
508

    
509
    /**
510
     * Checks if column is usable by going through all values of columns of a node in the original table.
511
     * @param columnNodeValues Values of a column node.
512
     * @return True if all values of a node are present in the original table columns.
513
     */
514
    private boolean checkIfColumnIsUsable(List<String> columnNodeValues) {
515
        // for each row in original table
516
        for (int i = 0; i < tableColumns.get(columnNames.get(0).getName()).getValues().size(); i++) {
517
            boolean isUsable = true;
518
            // check if a row has values that are stored in the column node (for each chosen column)
519
            for (int j = 0; j < columnNodeValues.size(); j++) {
520
                if (!tableColumns.get(columnNames.get(j).getName()).getValues().get(i).equals(columnNodeValues.get(j))) {
521
                    isUsable = false;
522
                    break;
523
                }
524
            }
525

    
526
            if (isUsable) {
527
                return true;
528
            }
529
        }
530

    
531
        return false;
532
    }
533

    
534
    /**
535
     * Computes value of the cell in regards to function.
536
     * @param valueFunction Column from which value should be taken and a function used and other info.
537
     * @param result Variable to which result is saved.
538
     * @param resultAvgSum Variable to which sum is saved of a function is AVG.
539
     * @param columnNode Column node of a cell.
540
     * @param isFirstMinMax Tells if the counted value is the first value of the MIN or MAX function.
541
     * @param usableID ID of the row in the original table.
542
     * @param isValueTotal Tells if a value is total value for the given function.
543
     * @param isSingleValueRow Tells whether a row only has single value (only represents one column of the original table).
544
     */
545
    private void computeValues(ValueFunction valueFunction, DoubleWrapper result, DoubleWrapper resultAvgSum, Node columnNode,
546
                               BooleanWrapper isFirstMinMax, int usableID, boolean isValueTotal, boolean isSingleValueRow) {
547
        switch (valueFunction.getFunction()) {
548
            case "COUNT":
549
                count(valueFunction, result, columnNode, isValueTotal, isSingleValueRow);
550
                break;
551
            case "SUM":
552
                sum(valueFunction, result, columnNode, usableID, isValueTotal, isSingleValueRow);
553
                break;
554
            case "MIN":
555
                min(valueFunction, result, columnNode, isFirstMinMax, usableID, isValueTotal, isSingleValueRow);
556
                break;
557
            case "MAX":
558
                max(valueFunction, result, columnNode, isFirstMinMax, usableID, isValueTotal, isSingleValueRow);
559
                break;
560
            case "AVG":
561
                average(valueFunction, result, resultAvgSum, columnNode, usableID, isValueTotal, isSingleValueRow);
562
                break;
563
        }
564
    }
565

    
566
    /**
567
     * Generates a cell with filled data.
568
     * @param function Name of the function.
569
     * @param totalResult Result of COUNT function to be saved.
570
     * @param totalResultSum Result of SUM function to be saved.
571
     * @param totalMin Result of MIN function to be saved.
572
     * @param totalMax Result of MAX function to be saved.
573
     * @param totalResultAvgSum Result of sum of AVG function to be saved.
574
     * @param totalResultAvg Result of count of AVG function to be saved.
575
     * @return Generated and filled cell.
576
     */
577
    private ContingencyTableRowCell generateFilledCell(String function, double totalResult, double totalResultSum,
578
                                                       double totalMin, double totalMax, double totalResultAvgSum,
579
                                                       double totalResultAvg) {
580
        ContingencyTableRowCell cell = null;
581
        switch (function) {
582
            case "COUNT":
583
                cell = new ContingencyTableRowCell(decimalFormat.format(totalResult), 1);
584
                break;
585
            case "SUM":
586
                cell = new ContingencyTableRowCell(decimalFormat.format(totalResultSum), 1);
587
                break;
588
            case "MIN":
589
                cell = new ContingencyTableRowCell(decimalFormat.format(totalMin), 1);
590
                break;
591
            case "MAX":
592
                cell = new ContingencyTableRowCell(decimalFormat.format(totalMax), 1);
593
                break;
594
            case "AVG":
595
                if (totalResultAvg == 0) {
596
                    cell = new ContingencyTableRowCell("!!Dělení nulou!!", 0);
597
                } else {
598
                    cell = new ContingencyTableRowCell(decimalFormat.format(totalResultAvgSum / totalResultAvg), 0);
599
                }
600
                break;
601
        }
602

    
603
        return cell;
604
    }
605

    
606
    /**
607
     * Initializes a filled of the values of a DoubleWrapper type.
608
     * @param field Field to be initialized.
609
     */
610
    private void initializeDoubleWrapperField(DoubleWrapper[] field) {
611
        for (int i = 0; i < field.length; i++) {
612
            field[i] = new DoubleWrapper(0);
613
        }
614
    }
615

    
616
    /**
617
     * Calculates COUNT function.
618
     * @param valueFunction Column from which value should be taken and other info.
619
     * @param result Variable to which result is saved.
620
     * @param columnNode Column node of a cell.
621
     * @param isValueTotal Tells if a value is total value for the given function.
622
     * @param isSingleValueRow Tells whether a row only has single value (only represents one column of the original table).
623
     */
624
    private void count(ValueFunction valueFunction, DoubleWrapper result, Node columnNode, boolean isValueTotal,
625
                       boolean isSingleValueRow) {
626
        result.setValue(result.getValue() + 1);
627
        if (columnNode != null && isSingleValueRow) {
628
            columnNode.setTotalResult(columnNode.getTotalResult() + 1);
629
        }
630
        if (isValueTotal && isSingleValueRow) {
631
            valueFunction.setTotalResult(valueFunction.getTotalResult() + 1);
632
        }
633
    }
634

    
635
    /**
636
     * Calculates SUM function.
637
     * @param valueFunction Column from which value should be taken and other info.
638
     * @param result Variable to which result is saved.
639
     * @param columnNode Column node of a cell.
640
     * @param usableID ID of the row in the original table.
641
     * @param isValueTotal Tells if a value is total value for the given function.
642
     * @param isSingleValueRow Tells whether a row only has single value (only represents one column of the original table).
643
     */
644
    private void sum(ValueFunction valueFunction, DoubleWrapper result, Node columnNode, int usableID, boolean isValueTotal,
645
                     boolean isSingleValueRow) {
646
        if (valueFunction.getType().equals("Číslo")) {
647
            double res = getDoubleValueFromTableCell(valueFunction, usableID);
648
            result.setValue(result.getValue() + res);
649
            if (columnNode != null && isSingleValueRow) {
650
                columnNode.setTotalResultSum(columnNode.getTotalResultSum() + res);
651
            }
652
            if (isValueTotal && isSingleValueRow) {
653
                valueFunction.setTotalResultSum(valueFunction.getTotalResultSum() + res);
654
            }
655
        }
656
    }
657

    
658
    /**
659
     * Calculates MIN function.
660
     * @param valueFunction Column from which value should be taken and other info.
661
     * @param result Variable to which result is saved.
662
     * @param columnNode Column node of a cell.
663
     * @param isFirstMinMax Tells if the counted value is the first value of the MIN or MAX function.
664
     * @param usableID ID of the row in the original table.
665
     * @param isValueTotal Tells if a value is total value for the given function.
666
     * @param isSingleValueRow Tells whether a row only has single value (only represents one column of the original table).
667
     */
668
    private void min(ValueFunction valueFunction, DoubleWrapper result, Node columnNode, BooleanWrapper isFirstMinMax,
669
                     int usableID, boolean isValueTotal, boolean isSingleValueRow) {
670
        if (valueFunction.getType().equals("Číslo")) {
671
            double res = getDoubleValueFromTableCell(valueFunction, usableID);
672
            // if min value of the cell has not been stored yet
673
            if (isFirstMinMax.isValue()) {
674
                isFirstMinMax.setValue(false);
675
                result.setValue(res);
676
            }
677
            else {
678
                result.setValue(Math.min(result.getValue(), res));
679
            }
680
            if (columnNode != null && isSingleValueRow) {
681
                // if min value of the entire column has not been stored yet
682
                if (columnNode.isFirstMin()) {
683
                    columnNode.setFirstMin(false);
684
                    columnNode.setTotalMin(res);
685
                } else {
686
                    columnNode.setTotalMin(Math.min(columnNode.getTotalMin(), res));
687
                }
688
            }
689
            if (isValueTotal && isSingleValueRow) {
690
                // if min value of the entire value column has not been stored yet
691
                if (valueFunction.isFirstMin()) {
692
                    valueFunction.setFirstMin(false);
693
                    valueFunction.setTotalMin(res);
694
                } else {
695
                    valueFunction.setTotalMin(Math.min(valueFunction.getTotalMin(), res));
696
                }
697
            }
698
        }
699
    }
700

    
701
    /**
702
     * Calculates MAX function.
703
     * @param valueFunction Column from which value should be taken and other info.
704
     * @param result Variable to which result is saved.
705
     * @param columnNode Column node of a cell.
706
     * @param isFirstMinMax Tells if the counted value is the first value of the MIN or MAX function.
707
     * @param usableID ID of the row in the original table.
708
     * @param isValueTotal Tells if a value is total value for the given function.
709
     * @param isSingleValueRow Tells whether a row only has single value (only represents one column of the original table).
710
     */
711
    private void max(ValueFunction valueFunction, DoubleWrapper result, Node columnNode, BooleanWrapper isFirstMinMax,
712
                     int usableID, boolean isValueTotal, boolean isSingleValueRow) {
713
        if (valueFunction.getType().equals("Číslo")) {
714
            double res = getDoubleValueFromTableCell(valueFunction, usableID);
715
            if (isFirstMinMax.isValue()) {
716
                // if max value of the cell has not been stored yet
717
                isFirstMinMax.setValue(false);
718
                result.setValue(res);
719
            }
720
            else {
721
                result.setValue(Math.max(result.getValue(), res));
722
            }
723
            if (columnNode != null && isSingleValueRow) {
724
                // if max value of the entire column has not been stored yet
725
                if (columnNode.isFirstMax()) {
726
                    columnNode.setFirstMax(false);
727
                    columnNode.setTotalMax(res);
728
                } else {
729
                    columnNode.setTotalMax(Math.max(columnNode.getTotalMax(), res));
730
                }
731
            }
732
            if (isValueTotal && isSingleValueRow) {
733
                // if max value of the entire value column has not been stored yet
734
                if (valueFunction.isFirstMax()) {
735
                    valueFunction.setFirstMax(false);
736
                    valueFunction.setTotalMax(res);
737
                } else {
738
                    valueFunction.setTotalMax(Math.max(valueFunction.getTotalMax(), res));
739
                }
740
            }
741
        }
742
    }
743

    
744
    /**
745
     * Calculates AVG function.
746
     * @param valueFunction Column from which value should be taken and other info.
747
     * @param result Variable to which count result is saved.
748
     * @param resultAvgSum Variable to which sum result is saved.
749
     * @param columnNode Column node of a cell.
750
     * @param usableID ID of the row in the original table.
751
     * @param isValueTotal Tells if a value is total value for the given function.
752
     * @param isSingleValueRow Tells whether a row only has single value (only represents one column of the original table).
753
     */
754
    private void average(ValueFunction valueFunction, DoubleWrapper result, DoubleWrapper resultAvgSum, Node columnNode,
755
                         int usableID, boolean isValueTotal, boolean isSingleValueRow) {
756
        if (valueFunction.getType().equals("Číslo")) {
757
            double res = getDoubleValueFromTableCell(valueFunction, usableID);
758
            result.setValue(result.getValue() + 1);
759
            resultAvgSum.setValue(resultAvgSum.getValue() + res);
760
            if (columnNode != null && isSingleValueRow) {
761
                columnNode.setTotalResultAvg(columnNode.getTotalResultAvg() + 1);
762
                columnNode.setTotalResultAvgSum(columnNode.getTotalResultAvgSum() + res);
763
            }
764
            if (isValueTotal && isSingleValueRow) {
765
                valueFunction.setTotalResultAvg(valueFunction.getTotalResultAvg() + 1);
766
                valueFunction.setTotalResultAvgSum(valueFunction.getTotalResultAvgSum() + res);
767
            }
768
        }
769
    }
770

    
771
    /**
772
     * Gets double value from a cell in a value column and usableID row in an original table
773
     * @param valueFunction Column from which value should be taken and other info.
774
     * @param usableID ID of the row in the original table.
775
     * @return Parsed double value.
776
     */
777
    private double getDoubleValueFromTableCell(ValueFunction valueFunction, int usableID) {
778
        double res;
779
        try {
780
            res = Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID));
781
        }
782
        catch (Exception e) {
783
            res = Double.NaN;
784
        }
785

    
786
        return res;
787
    }
788

    
789
    /**
790
     * Generates a final row, with total values of columns.
791
     * @return Final row filled with data.
792
     */
793
    private ContingencyTableRow createFinalRow() {
794
        ContingencyTableRow finalRow = new ContingencyTableRow(false, 0);
795
        finalRow.addTableRowCell(new ContingencyTableRowCell("Celkem", 1));
796

    
797
        if (valueFunctions.get(0).getFunction() != null && valueFunctions.get(0).getFunction().equals("NIC")) {
798
            return finalRow;
799
        }
800

    
801
        List<ContingencyTableRowCell> valueCells = new ArrayList<>();
802
        // for each selected function
803
        for (ValueFunction valueFunction : valueFunctions) {
804
            //for each column
805
            for (Node columnNode : columnNodes) {
806
                if (columnNode.isFilledTotalValue()) {
807
                    finalRow.addTableRowCell(generateFilledCell(valueFunction.getFunction(), columnNode.getTotalResult(), columnNode.getTotalResultSum(),
808
                            columnNode.getTotalMin(), columnNode.getTotalMax(), columnNode.getTotalResultAvgSum(),
809
                            columnNode.getTotalResultAvg()));
810
                }
811
                else {
812
                    finalRow.addTableRowCell(new ContingencyTableRowCell("", 0));
813
                }
814
            }
815
            if (valueFunction.isFilledTotalValue()) {
816
                valueCells.add(generateFilledCell(valueFunction.getFunction(), valueFunction.getTotalResult(), valueFunction.getTotalResultSum(),
817
                        valueFunction.getTotalMin(), valueFunction.getTotalMax(), valueFunction.getTotalResultAvgSum(),
818
                        valueFunction.getTotalResultAvg()));
819
            }
820
            else {
821
                valueCells.add(new ContingencyTableRowCell("", 0));
822
            }
823
        }
824

    
825
        // append value cells to the end
826
        finalRow.addTableRowCells(valueCells);
827

    
828
        return finalRow;
829
    }
830
}
(2-2/5)