commit 830a7e2e8b354062b08c9ee2a0d614d86827ebfa
Author: mlinha <mlinha@students.zcu.cz>
Date:   Tue Sep 1 17:07:12 2020 +0200

    re #8176 nothing; re #8178 added checks for data types; added table name to the generated table

diff --git a/src/main/java/vldc/aswi/database/DatabaseInterface.java b/src/main/java/vldc/aswi/database/DatabaseInterface.java
index 279dd373..fcd376aa 100644
--- a/src/main/java/vldc/aswi/database/DatabaseInterface.java
+++ b/src/main/java/vldc/aswi/database/DatabaseInterface.java
@@ -95,36 +95,43 @@ public class DatabaseInterface {
      * @return List of results saved as TableColumn.
      */
     public Map<String, TableColumn> executeQueryAndReturnTableColumns(String sqlQuery) {
-        return jdbcTemplate.query(
-                sqlQuery,
-                resultSet -> {
-                    List<TableColumn> tableColumnsList = new ArrayList<>();
-
-                    ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
-                    int columnCount = resultSetMetaData.getColumnCount();
+        try {
+            return jdbcTemplate.query(
+                    sqlQuery,
+                    resultSet -> {
+                        List<TableColumn> tableColumnsList = new ArrayList<>();
 
-                    // The column count starts from 1.
-                    for (int i = 1; i <= columnCount; i++ ) {
-                        String columnName = resultSetMetaData.getColumnName(i);
-                        tableColumnsList.add(new TableColumn(columnName));
-                    }
+                        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+                        int columnCount = resultSetMetaData.getColumnCount();
 
-                    // Add values from row to every column.
-                    while(resultSet.next()) {
+                        // The column count starts from 1.
                         for (int i = 1; i <= columnCount; i++) {
-                            tableColumnsList.get(i - 1).addValue(resultSet.getString(i));
+                            String columnName = resultSetMetaData.getColumnName(i);
+                            tableColumnsList.add(new TableColumn(columnName));
                         }
-                    }
 
-                    Map<String, TableColumn> tableColumnMap = new HashMap<>();
+                        // Add values from row to every column.
+                        while (resultSet.next()) {
+                            for (int i = 1; i <= columnCount; i++) {
+                                tableColumnsList.get(i - 1).addValue(resultSet.getString(i));
+                            }
+                        }
 
-                    // Create map, where key is tablecolumn name.
-                    for (TableColumn column : tableColumnsList) {
-                        tableColumnMap.put(column.getName(), column);
-                    }
+                        Map<String, TableColumn> tableColumnMap = new HashMap<>();
 
-                    return tableColumnMap;
-                }
-        );
+                        // Create map, where key is tablecolumn name.
+                        for (TableColumn column : tableColumnsList) {
+                            tableColumnMap.put(column.getName(), column);
+                        }
+
+                        return tableColumnMap;
+                    }
+            );
+        }
+        catch (DataAccessException e)
+            {
+                // If SQL query is not valid, then JdbcTemplate returned DataAccess exception.
+                return null;
+            }
     }
 }
diff --git a/src/main/java/vldc/aswi/service/ExportManagerImpl.java b/src/main/java/vldc/aswi/service/ExportManagerImpl.java
index e0e8d507..83839852 100644
--- a/src/main/java/vldc/aswi/service/ExportManagerImpl.java
+++ b/src/main/java/vldc/aswi/service/ExportManagerImpl.java
@@ -41,7 +41,7 @@ public class ExportManagerImpl implements ExportManager {
 
         // Get contingency table rows.
         List<ContingencyTableRow> contingencyTableRows = this.sqlQueryManager.getContingencyTableRow(newConfiguration.getAssembly(),
-                newConfiguration.getParametersInConfiguration(), isNotRemoveEmpty);
+                newConfiguration.getParametersInConfiguration(), isNotRemoveEmpty, newConfiguration.getTableName());
 
         try {
             File tmpFile = null;
diff --git a/src/main/java/vldc/aswi/service/SqlQueryManager.java b/src/main/java/vldc/aswi/service/SqlQueryManager.java
index f4305d39..7403f5d4 100644
--- a/src/main/java/vldc/aswi/service/SqlQueryManager.java
+++ b/src/main/java/vldc/aswi/service/SqlQueryManager.java
@@ -17,11 +17,12 @@ public interface SqlQueryManager {
      * @param assembly Assembly from which configuration was created.
      * @param parametersInConfiguration Parameters in configuration with their locations and operators.
      * @param isNotRemoveEmpty Sets if empty rows and columns should not be removed.
+     * @param tableName Name of the table.
      * @return List of contingencyTableRow.
      */
     List<ContingencyTableRow> getContingencyTableRow(Assembly assembly,
                                                      List<ParameterInConfiguration> parametersInConfiguration,
-                                                     boolean isNotRemoveEmpty);
+                                                     boolean isNotRemoveEmpty, String tableName);
 
     /**
      * Validate given SQL query.
diff --git a/src/main/java/vldc/aswi/service/SqlQueryManagerImpl.java b/src/main/java/vldc/aswi/service/SqlQueryManagerImpl.java
index bb4f9c65..80d0a1eb 100644
--- a/src/main/java/vldc/aswi/service/SqlQueryManagerImpl.java
+++ b/src/main/java/vldc/aswi/service/SqlQueryManagerImpl.java
@@ -5,7 +5,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import vldc.aswi.database.DatabaseInterface;
 import vldc.aswi.domain.Assembly;
-import vldc.aswi.domain.Configuration;
 import vldc.aswi.domain.Function;
 import vldc.aswi.domain.parameter.ParameterInConfiguration;
 import vldc.aswi.model.table.NameUserName;
@@ -35,18 +34,27 @@ public class SqlQueryManagerImpl implements SqlQueryManager {
      * @param assembly Assembly from which configuration was created.
      * @param parametersInConfiguration Parameters in configuration with their locations and operators.
      * @param isNotRemoveEmpty Sets if empty rows and columns should not be removed.
-     * @return List of contingencyTableRow.
+     * @param tableName Name of the table.
+     * @return List of contingencyTableRow or null, if error in SQL query occurred.
      */
     @Override
     public List<ContingencyTableRow> getContingencyTableRow(Assembly assembly,
                                                             List<ParameterInConfiguration> parametersInConfiguration,
-                                                            boolean isNotRemoveEmpty) {
+                                                            boolean isNotRemoveEmpty, String tableName) {
         long startTime = System.nanoTime();
 
         Map<String, TableColumn> tableColumns = this.databaseInterface.executeQueryAndReturnTableColumns(
                 generateFullSQLQuery(assembly.getSQLQuery(), parametersInConfiguration)
         );
 
+        if (tableColumns == null) {
+            return null;
+        }
+
+        if (tableColumns.values().iterator().next().getValues().size() == 0) {
+            return new ArrayList<>();
+        }
+
         long stopTime = System.nanoTime();
         System.out.println("Select:" + TimeUnit.MILLISECONDS.convert((stopTime - startTime), TimeUnit.NANOSECONDS));
         List<NameUserName> rowNames = new ArrayList<>();
@@ -61,7 +69,7 @@ public class SqlQueryManagerImpl implements SqlQueryManager {
 
         initializeContingencyTableDataLists(rowNames, columnNames, valueFunctions, copiedParametersInConfiguration);
 
-        Converter converter = new Converter(tableColumns, rowNames, columnNames, valueFunctions, isNotRemoveEmpty);
+        Converter converter = new Converter(tableColumns, rowNames, columnNames, valueFunctions, isNotRemoveEmpty, tableName);
         return converter.convertTableColumnsToContingencyTableRows();
     }
 
@@ -84,6 +92,13 @@ public class SqlQueryManagerImpl implements SqlQueryManager {
         return this.databaseInterface.getNameOfColumnsFromQuery(sqlQuery);
     }
 
+    /**
+     * Initializes list with values.
+     * @param rowNames List of selected rows.
+     * @param columnNames List of selected columns.
+     * @param valueFunctions List of selected values and functions.
+     * @param parametersInConfiguration List of parameters in configuration.
+     */
     private void initializeContingencyTableDataLists(List<NameUserName> rowNames, List<NameUserName> columnNames,
                                                      List<ValueFunction> valueFunctions, List<ParameterInConfiguration> parametersInConfiguration) {
         for (ParameterInConfiguration parameterInConfiguration : parametersInConfiguration) {
@@ -151,7 +166,7 @@ public class SqlQueryManagerImpl implements SqlQueryManager {
     }
 
     /**
-     * Precesses listing (enum) type.
+     * Processes listing (enum) type.
      * @param parameterInConfiguration Listing type parameter iin configuration.
      * @param finalSQLQuery Resulting SQL query.
      * @param isWhereDefined Variable storing information if WHERE clause was defined in the resulting SQL query.
@@ -240,6 +255,10 @@ public class SqlQueryManagerImpl implements SqlQueryManager {
         StringBuilder SQLQueryPart = new StringBuilder();
         SQLQueryPart.append("(");
         for (int i = 0; i < values.length; i++) {
+            if (type.equals("Datum")) {
+                SQLQueryPart.append("date ");
+            }
+
             addQuotes(type, SQLQueryPart);
             SQLQueryPart.append(values[i]);
             addQuotes(type, SQLQueryPart);
@@ -261,12 +280,20 @@ public class SqlQueryManagerImpl implements SqlQueryManager {
     private String betweenNotBetween(String[] values, String type) {
         StringBuilder SQLQueryPart = new StringBuilder();
 
+        if (type.equals("Datum")) {
+            SQLQueryPart.append("date ");
+        }
+
         addQuotes(type, SQLQueryPart);
         SQLQueryPart.append(values[0]);
         addQuotes(type, SQLQueryPart);
 
         SQLQueryPart.append(" AND ");
 
+        if (type.equals("Datum")) {
+            SQLQueryPart.append("date ");
+        }
+
         addQuotes(type, SQLQueryPart);
         SQLQueryPart.append(values[1]);
         addQuotes(type, SQLQueryPart);
@@ -283,6 +310,10 @@ public class SqlQueryManagerImpl implements SqlQueryManager {
     private String otherOperators(String[] values, String type) {
         StringBuilder SQLQueryPart = new StringBuilder();
 
+        if (type.equals("Datum")) {
+            SQLQueryPart.append("date ");
+        }
+
         addQuotes(type, SQLQueryPart);
         SQLQueryPart.append(values[0]);
         addQuotes(type, SQLQueryPart);
diff --git a/src/main/java/vldc/aswi/utils/Converter.java b/src/main/java/vldc/aswi/utils/Converter.java
index 1bd96164..dd3be9d5 100644
--- a/src/main/java/vldc/aswi/utils/Converter.java
+++ b/src/main/java/vldc/aswi/utils/Converter.java
@@ -8,6 +8,7 @@ import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Stack;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -15,6 +16,11 @@ import java.util.concurrent.TimeUnit;
  */
 public class Converter {
 
+    /**
+     * Name of the table
+     */
+    private final String tableName;
+
     /**
      * Stores information about whether user set switch to show empty rows and columns to ON.
      */
@@ -57,14 +63,16 @@ public class Converter {
      * @param columnNames List of names and user names of selected columns.
      * @param valueFunctions List of information about selected value and functions.
      * @param isNotRemoveEmpty Stores information about whether user set switch to show empty rows and columns to ON.
+     * @param tableName Name of the table.
      */
     public Converter(Map<String, TableColumn> tableColumns, List<NameUserName> rowNames, List<NameUserName> columnNames,
-                     List<ValueFunction> valueFunctions, boolean isNotRemoveEmpty) {
+                     List<ValueFunction> valueFunctions, boolean isNotRemoveEmpty, String tableName) {
         this.tableColumns = tableColumns;
         this.rowNames = rowNames;
         this.columnNames = columnNames;
         this.valueFunctions = valueFunctions;
         this.isNotRemoveEmpty = isNotRemoveEmpty;
+        this.tableName = tableName;
     }
 
     /**
@@ -148,44 +156,53 @@ public class Converter {
             ContingencyTableRow headerRow = headerRows.get(i);
 
             if (i == 0) {
-                headerRow.addTableRowCell(new ContingencyTableRowCell("Tabulka", 1));
+                headerRow.addTableRowCell(new ContingencyTableRowCell(tableName, 1));
             }
             else {
                 headerRow.addTableRowCell(new ContingencyTableRowCell("", 1));
             }
         }
 
-
-        List<String> prevValues = new ArrayList<>();
-        int prevSize = 0;
-        for (ValueFunction valueFunction : valueFunctions) {
+        List<String> prevValues = new ArrayList<>(); // values that were visited
+        int prevSize = 0; // number of previous column node values
+        for (ValueFunction valueFunction : valueFunctions) { // for each function
+            // add cell with the function name and value name to the 0th row with span 0
             headerRows.get(0).addTableRowCell(new ContingencyTableRowCell(valueFunction.getFunction() + " z " + valueFunction.getParameterName(), 0));
-            for (Node columnNode : columnNodes) {
-                for (int i = 0; i < columnNode.getValues().size(); i++) {
+            for (Node columnNode : columnNodes) { // for each column
+                for (int i = 0; i < columnNode.getValues().size(); i++) { // for each value in column
                     String value = columnNode.getValues().get(i);
-                    if (!prevValues.contains(value)) {
+                    // check if value is not in the visited list, if list size equals i, the value is not visited
+                    // eg. i = 0 -> no values in the list, if list size is bigger than i, there are more values than i
+                    // so then check if value at ith position is not equal to the current value (is not visited)
+                    if (prevValues.size() == i || (prevValues.size() > i && !prevValues.get(i).equals(value))) {
                         prevValues.add(value);
+                        // add cell with value with span 0 to the i + 1th row (0th is for function name and value name cells)
                         headerRows.get(i + 1).addTableRowCell(new ContingencyTableRowCell(columnNames.get(i).getUserName() + " - " + value, 1));
+                        // add 1 to the span of the last added function and value name cell
                         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);
                     } else if (columnNode.getValues().size() >= prevSize) {
+                        // add 1 to the span of the last added cell of the row of i + 1th value
                         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);
                     }
                 }
 
-                if (columnNode.getValues().size() < prevSize) {
+                if (columnNode.getValues().size() < prevSize) { // if there are less values than in previous column node
                     for (int s = prevSize; s < headerRows.size(); s++) {
+                        // add blank cell to the rows bellow
                         headerRows.get(s).addTableRowCell(new ContingencyTableRowCell("", 1));
                     }
                     for (int s = 1; s < columnNode.getValues().size(); s++) {
+                        // add 1 to colspan to the cells above
                         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);
                     }
+                    // create cell for total values (not for lists of the tree)
                     headerRows.get(columnNode.getValues().size()).addTableRowCell(new ContingencyTableRowCell(columnNode.getValues().get(columnNode.getValues().size() - 1) + " Celkem", 1));
                 }
 
                 prevSize = columnNode.getValues().size();
 
                 if (!prevValues.isEmpty()) {
-                    prevValues.remove(prevValues.size() - 1);
+                    prevValues.remove(prevValues.size() - 1); // remove last value
                 }
             }
         }
@@ -203,8 +220,8 @@ public class Converter {
 
     /**
      * Gets original values of a given list.
-     * @param list list from which original values should be taken
-     * @return list of original values
+     * @param list List from which original values should be taken.
+     * @return List of original values.
      */
     private List<String> getOriginalValuesFromList(List<String> list) {
         List<String> origList = new ArrayList<>();
@@ -220,7 +237,7 @@ public class Converter {
 
     /**
      * Creates data rows.
-     * @return list of contingency table rows
+     * @return List of contingency table rows.
      */
     private List<ContingencyTableRow> createDataRows() {
         List<ContingencyTableRow> dataRows = new ArrayList<>();
@@ -237,6 +254,7 @@ public class Converter {
         long stopTime = System.nanoTime();
         System.out.println("Rows List:" + TimeUnit.MILLISECONDS.convert((stopTime - startTime), TimeUnit.NANOSECONDS));
 
+        // loop through all generated rows and store unusable rows to the list rowsToBeRemoved
         List<ContingencyTableRow> rowsToBeRemoved = new ArrayList<>();
         for (int i = 0; i < dataRows.size(); i++) {
             if (!fillRowWithData(dataRows.get(i), rowNodes.get(i))) {
@@ -244,8 +262,10 @@ public class Converter {
             }
         }
 
+        // remove unusable rows
         dataRows.removeAll(rowsToBeRemoved);
 
+        // create final row (row with total values of each column)
         if (valueFunctions.get(0).getFunction() != null && (!valueFunctions.get(0).getFunction().equals("NIC") ||
                 (valueFunctions.get(0).getFunction().equals("NIC") && columnNames.size() > 0))) {
             dataRows.add(createFinalRow());
@@ -256,11 +276,11 @@ public class Converter {
 
     /**
      * Recursively generates all possible row combinations, sorted as in contingency table.
-     * @param rows list of sorted contingency table rows
-     * @param index index in the values list
-     * @param values list of lists of values of each query row
-     * @param rowNodes list of row nodes
-     * @param prevNode parent node of the nodes
+     * @param rows List of sorted contingency table rows.
+     * @param index Index in the values list.
+     * @param values List of lists of values of each query row.
+     * @param rowNodes List of row nodes.
+     * @param prevNode Parent node of the nodes.
      */
     private void generateRows(List<ContingencyTableRow> rows, int index, List<List<String>> values, List<Node> rowNodes,
                               Node prevNode) {
@@ -268,6 +288,7 @@ public class Converter {
             List<String> list = values.get(index);
             for (String s : list) {
                 StringBuilder levelString = new StringBuilder();
+                // add indent if row value has index higher than 0
                 for (int i = 0; i < index; i++) {
                     levelString.append("      ");
                 }
@@ -276,9 +297,12 @@ public class Converter {
                 row.addTableRowCell(new ContingencyTableRowCell(levelString.toString(), 1));
                 rows.add(row);
                 Node node = new Node(prevNode);
+                // save a value from a chosen row
                 node.getValues().add(s);
+                // visit (save) root
                 rowNodes.add(node);
                 int newIndex = index + 1;
+                // visit sub tree
                 generateRows(rows, newIndex, values, rowNodes, node);
             }
         }
@@ -286,25 +310,30 @@ public class Converter {
 
     /**
      * Recursively generates all possible column combinations, sorted as in contingency table.
-     * @param index index in the values list
-     * @param values list of lists of values of each query row
-     * @param columnNodes list of column nodes
-     * @param prevNode parent node of the nodes
+     * @param index Index in the values list.
+     * @param values List of lists of values of each query row.
+     * @param columnNodes List of column nodes.
+     * @param prevNode Parent node of the nodes.
      */
     private void generateColumns(int index, List<List<String>> values, List<Node> columnNodes, Node prevNode) {
         if (index < values.size()) {
             List<String> list = values.get(index);
             for (String s : list) {
                 Node node = new Node(prevNode);
+                // save a value from a chosen column
                 node.getValues().add(s);
                 if (isNotRemoveEmpty) {
                     int newIndex = index + 1;
+                    // first visit all subtrees
                     generateColumns(newIndex, values, columnNodes, node);
+                    // visit (save) root
                     columnNodes.add(node);
                 }
                 else if (checkIfColumnIsUsable(node.getValues())) {
                     int newIndex = index + 1;
+                    // first visit all subtrees
                     generateColumns(newIndex, values, columnNodes, node);
+                    // visit (save) root
                     columnNodes.add(node);
                 }
             }
@@ -313,69 +342,86 @@ public class Converter {
 
     /**
      * Fills each data row with data.
-     * @param row row to ne filled with data
-     * @param node node of the row
+     * @param row Row to be filled with data.
+     * @param rowNode Node of the row.
      */
-    private boolean fillRowWithData(ContingencyTableRow row, Node node) {
+    private boolean fillRowWithData(ContingencyTableRow row, Node rowNode) {
         if (valueFunctions.size() == 0) {
             return true;
         }
 
         boolean isSingleValueRow = false;
 
-        if (node.getValues().size() <= 1) {
+        // check if row is a root (single) value
+        if (rowNode.getValues().size() <= 1) {
             isSingleValueRow = true;
         }
 
+        // stores if row is usable
         boolean isRowUsable = false;
+
+        // if user selected an option to show all rows and columns, set that row is always usable
         if (isNotRemoveEmpty) {
             isRowUsable = true;
         }
 
+        // if no columns or functions were selected
         if (columnNodes.size() == 0) {
             columnNodes.add(new Node());
         }
 
-        List<Integer> usableIDs = getUsableIDs(node);
+        // load the list of usable original row IDs
+        List<Integer> usableIDs = getUsableIDs(rowNode.getValues());
 
         if (valueFunctions.get(0).getFunction() != null && valueFunctions.get(0).getFunction().equals("NIC") &&
-                !usableIDs.isEmpty()) {
+                !usableIDs.isEmpty()) { // return that row is usable because it is used for list generation
             return true;
         }
         else if (valueFunctions.get(0).getFunction() != null && valueFunctions.get(0).getFunction().equals("NIC") &&
-                usableIDs.isEmpty()) {
+                usableIDs.isEmpty()) { // no function selected and no usable rows in original return if row is usable
             return isRowUsable;
         }
 
+        // array of total values for functions
         DoubleWrapper[] totals = new DoubleWrapper[valueFunctions.size()];
         initializeDoubleWrapperField(totals);
 
+        // array of total sum values for functions if functions are AVG
         DoubleWrapper[] totalsAvgSum = new DoubleWrapper[valueFunctions.size()];
         initializeDoubleWrapperField(totalsAvgSum);
 
+        // array that stores if total value cell will be filled or left blank for current row and each function
         boolean[] isFilledValueCells = new boolean[valueFunctions.size()];
-        //hodnota
+        // for each function
         for (int v = 0; v < valueFunctions.size(); v++) {
+            // if function has not been selected
             if (valueFunctions.get(v).getFunction() != null && (valueFunctions.get(v).getFunction().equals("") ||
                     valueFunctions.get(v).getFunction().equals("NIC"))) {
                 isRowUsable = true;
                 break;
             }
+
+            // stores if first MIN or MAX total value was saved for current function
             BooleanWrapper isFirstMinMaxTotal = new BooleanWrapper(true);
+
             ValueFunction valueFunction = valueFunctions.get(v);
-            //sloupec v kont. tabulce
+            // for each contingency table column
             for (Node columnNode : columnNodes) {
+                // result that will be stored in a cell given by row and column rowNode
                 DoubleWrapper result = new DoubleWrapper(0);
+                // sum result used to calculate AVG result in a cell given by row and column rowNode
                 DoubleWrapper resultAvgSum = new DoubleWrapper(0);
+                // stores if first MIN or MAX value was saved for current cell
                 BooleanWrapper isFirstMinMax = new BooleanWrapper(true);
+                // stores if a cell will be filled or left blank
                 boolean isFilledCell = false;
-                // radek v orig. tabulce
+                // for each usable row
                 for (Integer usableID : usableIDs) {
                     boolean isUsable = true;
+                    // index of selected row
                     int j;
-                    // zvoleny sloupec ze sloupcu, ziska hodnoty z pouzitelne radky danych sloupcu originalni tabulky a
-                    // pokud se vsechny rovnaji hodnotam, ktere jsou ulozeny v hodnotach uzlu na pozici vybraneho
-                    // sloupce, je kommbinace pouzitelna k vypoctum
+                    // check if values of the contingency table column rowNode are present in the row (at usableID position
+                    // of original table) for columns that belong to column rowNode
                     for (j = 0; j < columnNode.getValues().size(); j++) {
                         if (!tableColumns.get(columnNames.get(j).getName()).getValues().get(usableID).equals(columnNode.getValues().get(j))) {
                             isUsable = false;
@@ -383,17 +429,19 @@ public class Converter {
                         }
                     }
 
-                    // zvoleny sloupec z hodnot
+                    // selected column from value
                     if (tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID) == null) {
                         isUsable = false;
                     }
 
-                    if (isUsable) { // pokud lze hodnotu pouzit, prunik vsech hodnot sedi (ma nejakou hodnotu)
+                    // if value is usable, "intersect" of values from contingency table row and column, and value is true
+                    if (isUsable) {
                         columnNode.setUsable(true);
                         isRowUsable = true;
                         isFilledCell = true;
                         computeValues(valueFunction, result, resultAvgSum, columnNode, isFirstMinMax,
                                 usableID, false, isSingleValueRow);
+                        // row is a root (single) value, save the result to the total function value cell
                         if (j <= 1) {
                             isFilledValueCells[v] = true;
                             computeValues(valueFunction, totals[v], totalsAvgSum[v], null, isFirstMinMaxTotal,
@@ -401,45 +449,51 @@ public class Converter {
                         }
                     }
                 }
+                // if cell should be blank, add empty value
                 if (!isFilledCell) {
                     row.addTableRowCell(new ContingencyTableRowCell("", 0));
                 }
-                else {
+                else { // generate a cell with a value
                     columnNode.setFilledTotalValue(true);
                     row.addTableRowCell(generateFilledCell(valueFunction.getFunction(), result.getValue(), result.getValue(), result.getValue(),
                             result.getValue(), resultAvgSum.getValue(), result.getValue()));
                 }
             }
         }
+        // generate cell for total function values
         for (int i = 0; i < valueFunctions.size(); i++) {
             if (valueFunctions.get(i).getFunction() != null && valueFunctions.get(i).getFunction().equals("NIC")) {
                 break;
             }
+            // if cell should be blank, add empty value
             if (!isFilledValueCells[i]) {
                 row.addTableRowCell(new ContingencyTableRowCell("", 0));
             }
-            else {
+            else { // generate a cell with a value
                 valueFunctions.get(i).setFilledTotalValue(true);
                 row.addTableRowCell(generateFilledCell(valueFunctions.get(i).getFunction(), totals[i].getValue(), totals[i].getValue(),
                         totals[i].getValue(), totals[i].getValue(), totalsAvgSum[i].getValue(), totals[i].getValue()));
             }
         }
 
+        // returns if row should be visible in the contingency table
         return isRowUsable;
     }
 
     /**
      * Gets a list of IDs of the query rows that fit the contingency table row settings.
-     * @param node node of the row
-     * @return list of usable IDs
+     * @param rowNodeValues Values of a row node.
+     * @return List of usable row IDs.
      */
-    private List<Integer> getUsableIDs(Node node) {
+    private List<Integer> getUsableIDs(List<String> rowNodeValues) {
         List<Integer> ids = new ArrayList<>();
 
+        // for each row in original table
         for (int i = 0; i < tableColumns.get(rowNames.get(0).getName()).getValues().size(); i++) {
             boolean isUsable = true;
-            for (int j = 0; j < node.getValues().size(); j++) {
-                if (!tableColumns.get(rowNames.get(j).getName()).getValues().get(i).equals(node.getValues().get(j))) {
+            // check if a row has values that are stored in the row node (for each chosen row)
+            for (int j = 0; j < rowNodeValues.size(); j++) {
+                if (!tableColumns.get(rowNames.get(j).getName()).getValues().get(i).equals(rowNodeValues.get(j))) {
                     isUsable = false;
                     break;
                 }
@@ -458,8 +512,10 @@ public class Converter {
      * @return True if all values of a node are present in the original table columns.
      */
     private boolean checkIfColumnIsUsable(List<String> columnNodeValues) {
+        // for each row in original table
         for (int i = 0; i < tableColumns.get(columnNames.get(0).getName()).getValues().size(); i++) {
             boolean isUsable = true;
+            // check if a row has values that are stored in the column node (for each chosen column)
             for (int j = 0; j < columnNodeValues.size(); j++) {
                 if (!tableColumns.get(columnNames.get(j).getName()).getValues().get(i).equals(columnNodeValues.get(j))) {
                     isUsable = false;
@@ -477,7 +533,7 @@ public class Converter {
 
     /**
      * Computes value of the cell in regards to function.
-     * @param valueFunction Function that should be computed for a cell.
+     * @param valueFunction Column from which value should be taken and a function used and other info.
      * @param result Variable to which result is saved.
      * @param resultAvgSum Variable to which sum is saved of a function is AVG.
      * @param columnNode Column node of a cell.
@@ -559,7 +615,7 @@ public class Converter {
 
     /**
      * Calculates COUNT function.
-     * @param valueFunction Function that should be computed for a cell.
+     * @param valueFunction Column from which value should be taken and other info.
      * @param result Variable to which result is saved.
      * @param columnNode Column node of a cell.
      * @param isValueTotal Tells if a value is total value for the given function.
@@ -578,7 +634,7 @@ public class Converter {
 
     /**
      * Calculates SUM function.
-     * @param valueFunction Function that should be computed for a cell.
+     * @param valueFunction Column from which value should be taken and other info.
      * @param result Variable to which result is saved.
      * @param columnNode Column node of a cell.
      * @param usableID ID of the row in the original table.
@@ -588,19 +644,20 @@ public class Converter {
     private void sum(ValueFunction valueFunction, DoubleWrapper result, Node columnNode, int usableID, boolean isValueTotal,
                      boolean isSingleValueRow) {
         if (valueFunction.getType().equals("Číslo")) {
-            result.setValue(result.getValue() + Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+            double res = getDoubleValueFromTableCell(valueFunction, usableID);
+            result.setValue(result.getValue() + res);
             if (columnNode != null && isSingleValueRow) {
-                columnNode.setTotalResultSum(columnNode.getTotalResultSum() + Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                columnNode.setTotalResultSum(columnNode.getTotalResultSum() + res);
             }
             if (isValueTotal && isSingleValueRow) {
-                valueFunction.setTotalResultSum(valueFunction.getTotalResultSum() + Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                valueFunction.setTotalResultSum(valueFunction.getTotalResultSum() + res);
             }
         }
     }
 
     /**
      * Calculates MIN function.
-     * @param valueFunction Function that should be computed for a cell.
+     * @param valueFunction Column from which value should be taken and other info.
      * @param result Variable to which result is saved.
      * @param columnNode Column node of a cell.
      * @param isFirstMinMax Tells if the counted value is the first value of the MIN or MAX function.
@@ -611,27 +668,31 @@ public class Converter {
     private void min(ValueFunction valueFunction, DoubleWrapper result, Node columnNode, BooleanWrapper isFirstMinMax,
                      int usableID, boolean isValueTotal, boolean isSingleValueRow) {
         if (valueFunction.getType().equals("Číslo")) {
+            double res = getDoubleValueFromTableCell(valueFunction, usableID);
+            // if min value of the cell has not been stored yet
             if (isFirstMinMax.isValue()) {
                 isFirstMinMax.setValue(false);
-                result.setValue(Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                result.setValue(res);
             }
             else {
-                result.setValue(Math.min(result.getValue(), Integer.parseInt(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID))));
+                result.setValue(Math.min(result.getValue(), res));
             }
             if (columnNode != null && isSingleValueRow) {
+                // if min value of the entire column has not been stored yet
                 if (columnNode.isFirstMin()) {
                     columnNode.setFirstMin(false);
-                    columnNode.setTotalMin(Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                    columnNode.setTotalMin(res);
                 } else {
-                    columnNode.setTotalMin(Math.min(columnNode.getTotalMin(), Integer.parseInt(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID))));
+                    columnNode.setTotalMin(Math.min(columnNode.getTotalMin(), res));
                 }
             }
             if (isValueTotal && isSingleValueRow) {
+                // if min value of the entire value column has not been stored yet
                 if (valueFunction.isFirstMin()) {
                     valueFunction.setFirstMin(false);
-                    valueFunction.setTotalMin(Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                    valueFunction.setTotalMin(res);
                 } else {
-                    valueFunction.setTotalMin(Math.min(valueFunction.getTotalMin(), Integer.parseInt(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID))));
+                    valueFunction.setTotalMin(Math.min(valueFunction.getTotalMin(), res));
                 }
             }
         }
@@ -639,7 +700,7 @@ public class Converter {
 
     /**
      * Calculates MAX function.
-     * @param valueFunction Function that should be computed for a cell.
+     * @param valueFunction Column from which value should be taken and other info.
      * @param result Variable to which result is saved.
      * @param columnNode Column node of a cell.
      * @param isFirstMinMax Tells if the counted value is the first value of the MIN or MAX function.
@@ -650,27 +711,31 @@ public class Converter {
     private void max(ValueFunction valueFunction, DoubleWrapper result, Node columnNode, BooleanWrapper isFirstMinMax,
                      int usableID, boolean isValueTotal, boolean isSingleValueRow) {
         if (valueFunction.getType().equals("Číslo")) {
+            double res = getDoubleValueFromTableCell(valueFunction, usableID);
             if (isFirstMinMax.isValue()) {
+                // if max value of the cell has not been stored yet
                 isFirstMinMax.setValue(false);
-                result.setValue(Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                result.setValue(res);
             }
             else {
-                result.setValue(Math.max(result.getValue(), Integer.parseInt(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID))));
+                result.setValue(Math.max(result.getValue(), res));
             }
             if (columnNode != null && isSingleValueRow) {
+                // if max value of the entire column has not been stored yet
                 if (columnNode.isFirstMax()) {
                     columnNode.setFirstMax(false);
-                    columnNode.setTotalMax(Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                    columnNode.setTotalMax(res);
                 } else {
-                    columnNode.setTotalMax(Math.max(columnNode.getTotalMax(), Integer.parseInt(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID))));
+                    columnNode.setTotalMax(Math.max(columnNode.getTotalMax(), res));
                 }
             }
             if (isValueTotal && isSingleValueRow) {
+                // if max value of the entire value column has not been stored yet
                 if (valueFunction.isFirstMax()) {
                     valueFunction.setFirstMax(false);
-                    valueFunction.setTotalMax(Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                    valueFunction.setTotalMax(res);
                 } else {
-                    valueFunction.setTotalMax(Math.max(valueFunction.getTotalMax(), Integer.parseInt(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID))));
+                    valueFunction.setTotalMax(Math.max(valueFunction.getTotalMax(), res));
                 }
             }
         }
@@ -678,7 +743,7 @@ public class Converter {
 
     /**
      * Calculates AVG function.
-     * @param valueFunction Function that should be computed for a cell.
+     * @param valueFunction Column from which value should be taken and other info.
      * @param result Variable to which count result is saved.
      * @param resultAvgSum Variable to which sum result is saved.
      * @param columnNode Column node of a cell.
@@ -689,19 +754,38 @@ public class Converter {
     private void average(ValueFunction valueFunction, DoubleWrapper result, DoubleWrapper resultAvgSum, Node columnNode,
                          int usableID, boolean isValueTotal, boolean isSingleValueRow) {
         if (valueFunction.getType().equals("Číslo")) {
+            double res = getDoubleValueFromTableCell(valueFunction, usableID);
             result.setValue(result.getValue() + 1);
-            resultAvgSum.setValue(resultAvgSum.getValue() + Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+            resultAvgSum.setValue(resultAvgSum.getValue() + res);
             if (columnNode != null && isSingleValueRow) {
                 columnNode.setTotalResultAvg(columnNode.getTotalResultAvg() + 1);
-                columnNode.setTotalResultAvgSum(columnNode.getTotalResultAvgSum() + Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                columnNode.setTotalResultAvgSum(columnNode.getTotalResultAvgSum() + res);
             }
             if (isValueTotal && isSingleValueRow) {
                 valueFunction.setTotalResultAvg(valueFunction.getTotalResultAvg() + 1);
-                valueFunction.setTotalResultAvgSum(valueFunction.getTotalResultAvgSum() + Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID)));
+                valueFunction.setTotalResultAvgSum(valueFunction.getTotalResultAvgSum() + res);
             }
         }
     }
 
+    /**
+     * Gets double value from a cell in a value column and usableID row in an original table
+     * @param valueFunction Column from which value should be taken and other info.
+     * @param usableID ID of the row in the original table.
+     * @return Parsed double value.
+     */
+    private double getDoubleValueFromTableCell(ValueFunction valueFunction, int usableID) {
+        double res;
+        try {
+            res = Double.parseDouble(tableColumns.get(valueFunction.getNameOfSelect()).getValues().get(usableID));
+        }
+        catch (Exception e) {
+            res = Double.NaN;
+        }
+
+        return res;
+    }
+
     /**
      * Generates a final row, with total values of columns.
      * @return Final row filled with data.
@@ -715,7 +799,9 @@ public class Converter {
         }
 
         List<ContingencyTableRowCell> valueCells = new ArrayList<>();
+        // for each selected function
         for (ValueFunction valueFunction : valueFunctions) {
+            //for each column
             for (Node columnNode : columnNodes) {
                 if (columnNode.isFilledTotalValue()) {
                     finalRow.addTableRowCell(generateFilledCell(valueFunction.getFunction(), columnNode.getTotalResult(), columnNode.getTotalResultSum(),
@@ -736,6 +822,7 @@ public class Converter {
             }
         }
 
+        // append value cells to the end
         finalRow.addTableRowCells(valueCells);
 
         return finalRow;
diff --git a/src/main/java/vldc/aswi/validators/ConfigurationValidator.java b/src/main/java/vldc/aswi/validators/ConfigurationValidator.java
index c43e16a8..ac4eceb5 100644
--- a/src/main/java/vldc/aswi/validators/ConfigurationValidator.java
+++ b/src/main/java/vldc/aswi/validators/ConfigurationValidator.java
@@ -69,7 +69,7 @@ public class ConfigurationValidator implements Validator {
      * @param operator Operator of the parameter in configuration.
      */
     private void validateBETWEEN(ParameterInConfiguration parameterInConfiguration, Errors errors, int i, String operator) {
-        if (!parameterInConfiguration.getOperatorValue().matches("\\w+;\\w+")) {
+        if (!parameterInConfiguration.getOperatorValue().matches(".+;.+")) {
             errors.rejectValue("parametersInConfiguration[" + i + "].operatorValue", "Nesprávně oddělené hodnoty" +
                     " filtru " + operator +"! Použijte ';' pro oddělení hodnot!");
         }
@@ -105,7 +105,7 @@ public class ConfigurationValidator implements Validator {
      * @param operator Operator of the parameter in configuration.
      */
     private void validateIN(ParameterInConfiguration parameterInConfiguration, Errors errors, int i, String operator) {
-        if (!parameterInConfiguration.getOperatorValue().matches("\\w+(;\\w+)*$")) {
+        if (!parameterInConfiguration.getOperatorValue().matches(".+(;.+)*$")) {
             errors.rejectValue("parametersInConfiguration[" + i + "].operatorValue", "Špatně zadaná hodnota" +
                     " filtru " + operator + "!  Použijte ';' pro oddělení hodnot!");
         }
diff --git a/src/main/java/vldc/aswi/web/controller/AssemblyController.java b/src/main/java/vldc/aswi/web/controller/AssemblyController.java
index d9f917c3..8e6c8607 100644
--- a/src/main/java/vldc/aswi/web/controller/AssemblyController.java
+++ b/src/main/java/vldc/aswi/web/controller/AssemblyController.java
@@ -157,6 +157,15 @@ public class AssemblyController extends BasicController {
      * Post method for form, where configuration is created from assembly.
      * @param newConfiguration - Configuration values.
      * @param bindingResult - Error results from assembly validators.
+     * @param atts Attributes.
+     * @param isNotRemoveEmptyString String that stores if switch to not remove empty rows and columns was set.
+     * @param id ID of the assembly.
+     * @param generateTable String used to check if generate table button was clicked.
+     * @param exportPdf String used to check if export to PDF button was clicked.
+     * @param exportXlsx String used to check if export to XLSX button was clicked.
+     * @param redirectAttributes Attributes.
+     * @param saveConfiguration String used to check if save configuration button was clicked.
+     * @param response HTTP servlet response.
      * @return ModelAndView for assembly.
      */
     @PostMapping("/assembly")
@@ -207,12 +216,26 @@ public class AssemblyController extends BasicController {
             }
             else {
                 List<ContingencyTableRow> rows = this.sqlQueryManager.getContingencyTableRow(newConfiguration.getAssembly(),
-                        newConfiguration.getParametersInConfiguration(), isNotRemoveEmpty);
-
-                modelMap.addAttribute("configurationID", id);
-                modelMap.addAttribute("contingencyTableRows", rows);
-                modelMap.addAttribute("isNotRemoveEmpty", isNotRemoveEmpty);
-                addConfigurationDataIntoModelAndView(newConfiguration, modelAndView, modelMap);
+                        newConfiguration.getParametersInConfiguration(), isNotRemoveEmpty, newConfiguration.getTableName());
+
+                if (rows != null) {
+                    if (rows.isEmpty()) {
+                        modelMap.addAttribute(assemblyErrorName, "Výsledná tabulka neobsahuje žádné hodnoty (" +
+                                "SQL dotaz nevrátil žádné řádky)!");
+                        modelMap.addAttribute(assemblyTitleName, generateTableTitleText + " " + newConfiguration.getName());
+                    }
+                    else {
+                        modelMap.addAttribute("configurationID", id);
+                        modelMap.addAttribute("contingencyTableRows", rows);
+                        modelMap.addAttribute("isNotRemoveEmpty", isNotRemoveEmpty);
+                        addConfigurationDataIntoModelAndView(newConfiguration, modelAndView, modelMap);
+                    }
+                }
+                else {
+                    modelMap.addAttribute(assemblyErrorName,  "Vyskytla se chyba v SQL dotazu! Prosím " +
+                            "zkontrolujte zadané hodnoty filtrů nebo kontaktujte administrátora!");
+                    modelMap.addAttribute(assemblyTitleName, generateTableTitleText + " " + newConfiguration.getName());
+                }
             }
         }
         else if (exportXlsx != null)
diff --git a/src/main/java/vldc/aswi/web/controller/ConfigurationController.java b/src/main/java/vldc/aswi/web/controller/ConfigurationController.java
index b62a3129..6f4128c9 100644
--- a/src/main/java/vldc/aswi/web/controller/ConfigurationController.java
+++ b/src/main/java/vldc/aswi/web/controller/ConfigurationController.java
@@ -72,6 +72,8 @@ public class ConfigurationController extends BasicController{
     /**
      * Opens an configuration page after configuration name was clicked
      * @param id ID of configuration
+     * @param errorMsg Error message text.
+     * @param successMsg Success message text.
      * @return modelAndView of page to be shown
      */
     @GetMapping("/configuration")
@@ -113,9 +115,17 @@ public class ConfigurationController extends BasicController{
     /**
      * Saves or edits configuration and redirects to the page with configuration
      * @param newConfiguration contained with configuration data
-     * @param id ID of configuration, if empty new configuration is created
-     * @param bindingResult binding result
-     * @return modelAndView with redirection
+     * @param bindingResult - Error results from assembly validators.
+     * @param isNotRemoveEmptyString String that stores if switch to not remove empty rows and columns was set.
+     * @param id ID of the assembly.
+     * @param generateTable String used to check if generate table button was clicked.
+     * @param exportPdf String used to check if export to PDF button was clicked.
+     * @param exportXlsx String used to check if export to XLSX button was clicked.
+     * @param redirectAttributes Attributes.
+     * @param saveConfiguration String used to check if save configuration button was clicked.
+     * @param response HTTP servlet response.
+     * @param request HTTP servlet request.
+     * @return ModelAndView for assembly.
      */
     @PostMapping("/configuration")
     public ModelAndView configurationPost(@Valid Configuration newConfiguration,
@@ -155,11 +165,26 @@ public class ConfigurationController extends BasicController{
             }
             else {
                 List<ContingencyTableRow> rows = this.sqlQueryManager.getContingencyTableRow(newConfiguration.getAssembly(),
-                        newConfiguration.getParametersInConfiguration(), isNotRemoveEmpty);
-
-                modelMap.addAttribute("configurationID", id);
-                modelMap.addAttribute("contingencyTableRows", rows);
-                addConfigurationDataIntoModelAndView(newConfiguration, modelAndView, modelMap);
+                        newConfiguration.getParametersInConfiguration(), isNotRemoveEmpty, newConfiguration.getTableName());
+
+                if (rows != null) {
+                    if (rows.isEmpty()) {
+                        modelMap.addAttribute(assemblyErrorName, "Výsledná tabulka neobsahuje žádné hodnoty (" +
+                                "SQL dotaz nevrátil žádné řádky)!");
+                        modelMap.addAttribute(assemblyTitleName, generateTableTitleText + " " + newConfiguration.getName());
+                    }
+                    else {
+                        modelMap.addAttribute("configurationID", id);
+                        modelMap.addAttribute("contingencyTableRows", rows);
+                        modelMap.addAttribute("isNotRemoveEmpty", isNotRemoveEmpty);
+                        addConfigurationDataIntoModelAndView(newConfiguration, modelAndView, modelMap);
+                    }
+                }
+                else {
+                    modelMap.addAttribute(assemblyErrorName, "Vyskytla se chyba v SQL dotazu! Prosím " +
+                            "zkontrolujte zadané hodnoty filtrů nebo kontaktujte administrátora!");
+                    modelMap.addAttribute(assemblyTitleName, generateTableTitleText + " " + newConfiguration.getName());
+                }
             }
         }
         else if (exportXlsx != null)
@@ -195,6 +220,7 @@ public class ConfigurationController extends BasicController{
     /**
      * Get method for configuration delete.
      * @param id - ID of configuration.
+     * @param redirectAttributes Attributes.
      * @return ModelAndView for index.
      */
     @GetMapping("/configuration_delete")
