commit 7638be72426179285973708d21308509b50c90f8
Author: Vojtěch Danišík <32537562+danisik@users.noreply.github.com>
Date:   Fri Apr 24 17:17:27 2020 +0200

    re #7886 Assembly validator completed (validating sql query, parameter name in select and parameter location) + displaying temp error message.
    Added title for edit / add assembly forms.
    Added toString method for all domains.

diff --git a/src/main/java/vldc/aswi/database/DatabaseInterface.java b/src/main/java/vldc/aswi/database/DatabaseInterface.java
index b3f78eff..d7769239 100644
--- a/src/main/java/vldc/aswi/database/DatabaseInterface.java
+++ b/src/main/java/vldc/aswi/database/DatabaseInterface.java
@@ -31,19 +31,60 @@ public class DatabaseInterface {
         this.jdbcTemplate = jdbcTemplate;
     }
 
+    /**
+     * Get list of table column names from SQL query.
+     * @param sqlQuery - SQL query.
+     * @return List of table column names. If null is returned, then it means that SQL query is not valid.
+     */
+    public List<String> getNameOfColumnsFromQuery(String sqlQuery) {
+        try
+        {
+            // Try extract columns from database using SQL query.
+            return jdbcTemplate.query(
+                sqlQuery,
+                new ResultSetExtractor<List<String>>() {
+                    @Override
+                    public List<String> extractData(ResultSet resultSet) throws SQLException {
+                        List<String> tableColumnNames = new ArrayList<>();
+
+                        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+                        int columnCount = resultSetMetaData.getColumnCount();
+
+                        // The column count starts from 1.
+                        for (int i = 1; i <= columnCount; i++ ) {
+                            String columnName = resultSetMetaData.getColumnName(i);
+                            tableColumnNames.add(columnName);
+                        }
+
+                        return tableColumnNames;
+                    }
+                }
+            );
+
+        }
+        catch (DataAccessException e)
+        {
+            // If SQL query is not valid, then JdbcTemplate returned DataAccess exception.
+            return null;
+        }
+    }
+
     /**
      * Validate given SQLQuery.
      * @param sqlQuery - Tested SQL Query.
      * @return true if SQL query is valid, false if not.
      */
     public boolean validateSQLQuery(String sqlQuery) {
+
         try
         {
+            // Try validate SQL query.
             jdbcTemplate.execute("EXPLAIN PLAN FOR " + sqlQuery);
             return true;
         }
         catch (DataAccessException e)
         {
+            // If SQL query is not valid, then JdbcTemplate returned DataAccess exception.
             return false;
         }
     }
@@ -70,6 +111,7 @@ public class DatabaseInterface {
                             tableColumnsList.add(new TableColumn(columnName));
                         }
 
+                        // 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));
@@ -78,6 +120,7 @@ public class DatabaseInterface {
 
                         Map<String, TableColumn> tableColumnMap = new HashMap<>();
 
+                        // Create map, where key is tablecolumn name.
                         for (TableColumn column : tableColumnsList) {
                             tableColumnMap.put(column.getName(), column);
                         }
diff --git a/src/main/java/vldc/aswi/domain/Assembly.java b/src/main/java/vldc/aswi/domain/Assembly.java
index d468500e..ec4527c0 100644
--- a/src/main/java/vldc/aswi/domain/Assembly.java
+++ b/src/main/java/vldc/aswi/domain/Assembly.java
@@ -74,4 +74,13 @@ public class Assembly extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "Assembly[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/domain/Configuration.java b/src/main/java/vldc/aswi/domain/Configuration.java
index eb7ca3a5..43168a72 100644
--- a/src/main/java/vldc/aswi/domain/Configuration.java
+++ b/src/main/java/vldc/aswi/domain/Configuration.java
@@ -58,4 +58,13 @@ public class Configuration extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "Configuration[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/domain/Function.java b/src/main/java/vldc/aswi/domain/Function.java
index 90940f4d..619399ac 100644
--- a/src/main/java/vldc/aswi/domain/Function.java
+++ b/src/main/java/vldc/aswi/domain/Function.java
@@ -50,4 +50,13 @@ public class Function extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "Function[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/domain/Location.java b/src/main/java/vldc/aswi/domain/Location.java
index 81ac7104..05bf1c38 100644
--- a/src/main/java/vldc/aswi/domain/Location.java
+++ b/src/main/java/vldc/aswi/domain/Location.java
@@ -47,4 +47,13 @@ public class Location extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "Location[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/domain/Operator.java b/src/main/java/vldc/aswi/domain/Operator.java
index f3083040..ff61fb1d 100644
--- a/src/main/java/vldc/aswi/domain/Operator.java
+++ b/src/main/java/vldc/aswi/domain/Operator.java
@@ -47,4 +47,13 @@ public class Operator extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "Operator[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/domain/Role.java b/src/main/java/vldc/aswi/domain/Role.java
index f45a8530..a20e2fb6 100644
--- a/src/main/java/vldc/aswi/domain/Role.java
+++ b/src/main/java/vldc/aswi/domain/Role.java
@@ -45,4 +45,13 @@ public class Role extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "Role[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/domain/User.java b/src/main/java/vldc/aswi/domain/User.java
index dac0f40c..a959a04f 100644
--- a/src/main/java/vldc/aswi/domain/User.java
+++ b/src/main/java/vldc/aswi/domain/User.java
@@ -54,4 +54,13 @@ public class User extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "User[]";
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/vldc/aswi/domain/parameter/Parameter.java b/src/main/java/vldc/aswi/domain/parameter/Parameter.java
index 9aa4236f..5c8a206f 100644
--- a/src/main/java/vldc/aswi/domain/parameter/Parameter.java
+++ b/src/main/java/vldc/aswi/domain/parameter/Parameter.java
@@ -95,12 +95,12 @@ public class Parameter extends EntityParent {
     }
 
     /**
-     * Overriding toString method to prevent StackOverflowError when parsing values from assembly form.
-     * @return Parameter info.
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
      */
     @Override
     public String toString() {
-        return "Parameter[name=" + name + ", nameOfSelect=" + nameOfSelect + ", defaultHodnota=" + defaultValue + "]";
+        return "Parameter[]";
     }
 
     /**
diff --git a/src/main/java/vldc/aswi/domain/parameter/ParameterInConfiguration.java b/src/main/java/vldc/aswi/domain/parameter/ParameterInConfiguration.java
index 476adebc..931735a6 100644
--- a/src/main/java/vldc/aswi/domain/parameter/ParameterInConfiguration.java
+++ b/src/main/java/vldc/aswi/domain/parameter/ParameterInConfiguration.java
@@ -75,4 +75,13 @@ public class ParameterInConfiguration extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "ParameterInConfiguration[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/domain/parameter/ParameterType.java b/src/main/java/vldc/aswi/domain/parameter/ParameterType.java
index 517b7cb0..966f42b3 100644
--- a/src/main/java/vldc/aswi/domain/parameter/ParameterType.java
+++ b/src/main/java/vldc/aswi/domain/parameter/ParameterType.java
@@ -45,4 +45,13 @@ public class ParameterType extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "ParameterType[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/domain/parameter/ParameterValue.java b/src/main/java/vldc/aswi/domain/parameter/ParameterValue.java
index ab3d0d13..a6197a16 100644
--- a/src/main/java/vldc/aswi/domain/parameter/ParameterValue.java
+++ b/src/main/java/vldc/aswi/domain/parameter/ParameterValue.java
@@ -43,4 +43,13 @@ public class ParameterValue extends EntityParent {
     public boolean equals(Object obj) {
         return super.equals(obj);
     }
+
+    /**
+     * Overriding toString method to prevent StackOverflowError.
+     * @return Object with zero parameters.
+     */
+    @Override
+    public String toString() {
+        return "ParameterValue[]";
+    }
 }
diff --git a/src/main/java/vldc/aswi/service/AssemblyManagerImpl.java b/src/main/java/vldc/aswi/service/AssemblyManagerImpl.java
index 3bcf630c..2b3f846e 100644
--- a/src/main/java/vldc/aswi/service/AssemblyManagerImpl.java
+++ b/src/main/java/vldc/aswi/service/AssemblyManagerImpl.java
@@ -85,7 +85,6 @@ public class AssemblyManagerImpl implements AssemblyManager {
      */
     @Override
     public Long updateAssembly(Assembly updatedAssemblyValues) {
-        // TODO: test it.
         Assembly assembly = getAssemblyById(updatedAssemblyValues.getId());
         assembly.setName(updatedAssemblyValues.getName());
         assembly.setSQLQuery(updatedAssemblyValues.getSQLQuery());
@@ -104,7 +103,6 @@ public class AssemblyManagerImpl implements AssemblyManager {
      * @param updatedRoles - List of roles.
      */
     private void updateAssemblyRoles(Long id, List<Role> updatedRoles) {
-        // TODO: test it.
         Assembly assembly = getAssemblyById(id);
         List<Role> currentRoles = assembly.getRoles();
 
diff --git a/src/main/java/vldc/aswi/service/SqlQueryManager.java b/src/main/java/vldc/aswi/service/SqlQueryManager.java
index 178d5277..a0285a1a 100644
--- a/src/main/java/vldc/aswi/service/SqlQueryManager.java
+++ b/src/main/java/vldc/aswi/service/SqlQueryManager.java
@@ -22,4 +22,11 @@ public interface SqlQueryManager {
      * @return true if SQL query is ok, false if not.
      */
     boolean validateSQLQuery(String sqlQuery);
+
+    /**
+     * Get list of table column names from SQL query.
+     * @param sqlQuery - SQL query.
+     * @return List of table column names.
+     */
+    List<String> getNameOfColumnsFromQuery(String sqlQuery);
 }
diff --git a/src/main/java/vldc/aswi/service/SqlQueryManagerImpl.java b/src/main/java/vldc/aswi/service/SqlQueryManagerImpl.java
index 70f2440b..26e3e364 100644
--- a/src/main/java/vldc/aswi/service/SqlQueryManagerImpl.java
+++ b/src/main/java/vldc/aswi/service/SqlQueryManagerImpl.java
@@ -30,7 +30,7 @@ public class SqlQueryManagerImpl implements SqlQueryManager{
      */
     @Override
     public List<ContingencyTableRow> getContingencyTableRow(String sqlQuery) {
-        Map<String, TableColumn> tableColumns = databaseInterface.executeQueryAndReturnTableColumns(sqlQuery);
+        Map<String, TableColumn> tableColumns = this.databaseInterface.executeQueryAndReturnTableColumns(sqlQuery);
         return Converter.convertTableColumnsToContingencyTableRows(tableColumns);
     }
 
@@ -41,6 +41,15 @@ public class SqlQueryManagerImpl implements SqlQueryManager{
      */
     @Override
     public boolean validateSQLQuery(String sqlQuery) {
-        return databaseInterface.validateSQLQuery(sqlQuery);
+        return this.databaseInterface.validateSQLQuery(sqlQuery);
+    }
+
+    /**
+     * Get list of table column names from SQL query.
+     * @param sqlQuery - SQL query.
+     * @return List of table column names.
+     */
+    public List<String> getNameOfColumnsFromQuery(String sqlQuery) {
+        return this.databaseInterface.getNameOfColumnsFromQuery(sqlQuery);
     }
 }
diff --git a/src/main/java/vldc/aswi/service/parameter/ParameterManagerImpl.java b/src/main/java/vldc/aswi/service/parameter/ParameterManagerImpl.java
index 7a313103..f207e925 100644
--- a/src/main/java/vldc/aswi/service/parameter/ParameterManagerImpl.java
+++ b/src/main/java/vldc/aswi/service/parameter/ParameterManagerImpl.java
@@ -106,6 +106,7 @@ public class ParameterManagerImpl implements ParameterManager {
             List<Location> locationList = new LinkedList<>();
             List<Function> functionList = new LinkedList<>();
             List<Operator> operatorList = new LinkedList<>();
+            List<ParameterValue> valueList = new LinkedList<>();
 
             // Try to put all locations into list.
             if (parameterValues.getLocations() != null && parameterValues.getLocations().size() > 0) {
@@ -128,10 +129,18 @@ public class ParameterManagerImpl implements ParameterManager {
                 }
             }
 
+            // Try to put all operators into list.
+            if (parameterValues.getParameterValues() != null && parameterValues.getParameterValues().size() > 0) {
+                for (ParameterValue parameterValue : parameterValues.getParameterValues()) {
+                    valueList.add(this.parameterValueRepository.getById(parameterValue.getId()));
+                }
+            }
+
             // Set lists of values into parameter.
             parameter.setLocations(locationList);
             parameter.setFunctions(functionList);
             parameter.setOperators(operatorList);
+            parameter.setParameterValues(valueList);
 
             // Insert parameter into database.
             this.parameterRepository.save(parameter);
@@ -145,7 +154,6 @@ public class ParameterManagerImpl implements ParameterManager {
      */
     @Override
     public void updateParameters(Long assemblyId, List<Parameter> newAssemblyParameters) {
-        // TODO: test it.
         Assembly assembly = this.assemblyRepository.getById(assemblyId);
         List<Parameter> currentAssemblyParameters = assembly.getParameters();
 
@@ -165,7 +173,7 @@ public class ParameterManagerImpl implements ParameterManager {
         newAssemblyParametersUpdated.retainAll(currentAssemblyParametersUpdated);
 
         // If no parameter has been deleted or added, then updating is unnecessary.
-        if (newAssemblyParametersNew.size() == 0 && currentAssemblyParametersDeleted.size() == 0 && newAssemblyParametersUpdated.size() == currentAssemblyParameters.size()) return;
+        if (newAssemblyParametersNew.size() == 0 && currentAssemblyParametersDeleted.size() == 0 && newAssemblyParametersUpdated.size() == 0) return;
 
         // Remove deleted parameters.
         for (Parameter parameter : currentAssemblyParametersDeleted) {
@@ -190,7 +198,6 @@ public class ParameterManagerImpl implements ParameterManager {
      * @param parameterValuesList - List of updated parameters.
      */
     private void updateParameters(List<Parameter> parameterValuesList) {
-        // TODO: test it.
         for (Parameter parameterValues : parameterValuesList) {
             // Get current parameter from database.
             Parameter parameter = this.parameterRepository.getById(parameterValues.getId());
@@ -218,7 +225,6 @@ public class ParameterManagerImpl implements ParameterManager {
      * @param updatedLocations - List of locations.
      */
     private void updateParameterLocations(Long id, List<Location> updatedLocations) {
-        // TODO: test it.
         Parameter parameter = this.parameterRepository.getById(id);
         List<Location> currentLocations = parameter.getLocations();
 
@@ -253,7 +259,6 @@ public class ParameterManagerImpl implements ParameterManager {
      * @param updatedFunctions - List of functions.
      */
     private void updateParameterFunctions(Long id, List<Function> updatedFunctions) {
-        // TODO: test it.
         Parameter parameter = this.parameterRepository.getById(id);
         List<Function> currentFunctions = parameter.getFunctions();
 
@@ -288,7 +293,6 @@ public class ParameterManagerImpl implements ParameterManager {
      * @param updatedOperators - List of operators.
      */
     private void updateParameterOperators(Long id, List<Operator> updatedOperators) {
-        // TODO: test it.
         Parameter parameter = this.parameterRepository.getById(id);
         List<Operator> currentOperators = parameter.getOperators();
 
@@ -323,7 +327,6 @@ public class ParameterManagerImpl implements ParameterManager {
      * @param newParameterValues - List of parameter values.
      */
     private void updateParameterValues(Long parameterId, List<ParameterValue> newParameterValues) {
-        // TODO: test it.
         Parameter parameter = this.parameterRepository.getById(parameterId);
         List<ParameterValue> currentParameterValues = parameter.getParameterValues();
 
@@ -343,7 +346,7 @@ public class ParameterManagerImpl implements ParameterManager {
         newParameterValuesUpdated.retainAll(currentParameterValuesUpdated);
 
         // If there is no changes, do not update.
-        if (newParameterValuesNew.size() == 0 && currentParameterValuesDeleted.size() == 0 && newParameterValuesUpdated.size() == currentParameterValues.size()) return;
+        if (newParameterValuesNew.size() == 0 && currentParameterValuesDeleted.size() == 0 && newParameterValuesUpdated.size() == 0) return;
 
         // Delete removed parameter values.
         for (ParameterValue parameterValue : currentParameterValuesDeleted) {
@@ -362,7 +365,6 @@ public class ParameterManagerImpl implements ParameterManager {
      * @param updatedParameterValuesList - List of all updated parameter values.
      */
     private void updateParameterValueValues(List<ParameterValue> updatedParameterValuesList) {
-        // TODO: test it.
         for (ParameterValue parameterValueValues : updatedParameterValuesList) {
             ParameterValue parameterValue = this.parameterValueRepository.getById(parameterValueValues.getId());
             parameterValue.setValue(parameterValueValues.getValue());
@@ -378,7 +380,6 @@ public class ParameterManagerImpl implements ParameterManager {
      * @param newParameterValuesNew - List of new parameter values.
      */
     private void addParameterValueValues(Long parameterId, List<ParameterValue> newParameterValuesNew) {
-        // TODO: test it.
         Parameter parameter = this.parameterRepository.getById(parameterId);
 
         for (ParameterValue parameterValueValues : newParameterValuesNew) {
diff --git a/src/main/java/vldc/aswi/validators/AssemblyValidator.java b/src/main/java/vldc/aswi/validators/AssemblyValidator.java
index 1fb62d96..abedb6fb 100644
--- a/src/main/java/vldc/aswi/validators/AssemblyValidator.java
+++ b/src/main/java/vldc/aswi/validators/AssemblyValidator.java
@@ -8,6 +8,8 @@ import vldc.aswi.domain.Assembly;
 import vldc.aswi.domain.parameter.Parameter;
 import vldc.aswi.service.SqlQueryManager;
 
+import java.util.List;
+
 /**
  * Validator for assembly.
  */
@@ -25,7 +27,7 @@ public class AssemblyValidator implements Validator {
      */
     @Override
     public boolean supports(Class<?> aClass) {
-        return Assembly.class.equals(aClass);
+        return Assembly.class.isAssignableFrom(aClass);
     }
 
     /**
@@ -35,43 +37,30 @@ public class AssemblyValidator implements Validator {
      */
     @Override
     public void validate(Object obj, Errors errors) {
-        // TODO: VALIDATING   - https://docs.spring.io/spring/docs/4.1.x/spring-framework-reference/html/validation.html
-        // TODO: ERROR STYLES - https://mkyong.com/spring-mvc/spring-mvc-form-errors-tag-example/
 
         // Get assembly values.
         Assembly assembly = (Assembly) obj;
 
         // Validate SQL Query.
-        boolean isSQLQueryValid = this.sqlQueryManager.validateSQLQuery(assembly.getSQLQuery());
+        List<String> columnNames = this.sqlQueryManager.getNameOfColumnsFromQuery(assembly.getSQLQuery());
 
-        if (!isSQLQueryValid) {
+        if (columnNames == null) {
             errors.rejectValue("SQLQuery", "SQL Query není validní!");
-            // Field error in object 'assembly' on field 'SQLQuery': rejected value [SELECT SP.fakulta_S as fakulta_studia,
-            // SP.kodSP_i as program_studia, fn_meaning2(SP.typ,'TYP_OBORU') as typ_studia, fn_meaning2(SP.forma,'FORMA_OBORU_NEW')
-            // as forma_studia, OS.prijmeni || ' ' || OS.jmeno as osoba, trunc((sysdate - OS.datum_naroz) / 365) as vek, 1 as pocet
-            // FROM studijni_programy SP, studenti ST, osoby OS WHERE SP.stpridno = ST.stpridno AND ST.osobidno = OS.osobidno AND
-            // exists (SELECT 'a' FROM studenti_v_roce SVR WHERE ST.os_cislo = SVR.os_cislo AND SVR.rok_platnosti >= '2018') AND
-            // ROWNUM <= 50];
-            // codes [SQL Query není validní!.assembly.SQLQuery,SQL Query není validní!.
-            // SQLQuery,SQL Query není validní!.java.lang.String,SQL Query není validní!]; arguments []; default message [null]
         }
 
-        // TODO: test validation for new assembly -> especially for parameters.
-        for (int i = 0; i < assembly.getParameters().size(); i++) {
-            // TODO: Parametr - Pokud název v SQL nebude v dotazu SQL, bude vyhozena chyba.
-            // TODO: Maybe nejdříve vzít všechny sloupce a potom zkontrolovat jestli obsahuje ten vybranej sloupec ?
-            // TODO: A kontrolovat to podle ".. as NAZEV_SLOUPCE" nebo jako "SP.TYP_STUDIA ...." ???
+        if (assembly.getParameters() != null && assembly.getParameters().size() > 0) {
+            for (int i = 0; i < assembly.getParameters().size(); i++) {
+                Parameter parameter = assembly.getParameters().get(i);
 
-            /*
-            if (!isParameterNameInSQLQuery) {
-                errors.rejectValue("parameterSQLName" + i, "Název parametru v SQL query není validní!");
-            }
-             */
+                // Check if parameter's nameOfSelect is contained in column names of SQL query.
+                if (parameter.getNameOfSelect() == null || !columnNames.stream().anyMatch(parameter.getNameOfSelect()::equalsIgnoreCase)) {
+                    errors.reject("parameters[" + i + "].nameOfSelect", "Název parametru v SQL query není validní u parametru '" + parameter.getName() + "'!");
+                }
 
-            Parameter parameter = assembly.getParameters().get(i);
-            // Check if current parameter have at least 1 location.
-            if (parameter == null || parameter.getLocations() == null || parameter.getLocations().size() == 0) {
-                errors.rejectValue("parameters[" + i + "].locations", "Není vybrána žádná lokace!");
+                // Check if current parameter have at least 1 location.
+                if (parameter.getLocations() == null || parameter.getLocations().size() == 0) {
+                    errors.rejectValue("parameters[" + i + "].locations", "", "Není vybrána žádná lokace u parametru '" + parameter.getName() + "'!");
+                }
             }
         }
     }
diff --git a/src/main/java/vldc/aswi/web/controller/AssemblyController.java b/src/main/java/vldc/aswi/web/controller/AssemblyController.java
index 876a8e73..3b9acb8d 100644
--- a/src/main/java/vldc/aswi/web/controller/AssemblyController.java
+++ b/src/main/java/vldc/aswi/web/controller/AssemblyController.java
@@ -23,7 +23,7 @@ import javax.validation.Valid;
  * Controller for assembly.
  */
 @Controller
-public class AssemblyController {
+public class AssemblyController extends BasicController {
 
     /** Autowired sql query manager. */
     @Autowired
@@ -61,6 +61,18 @@ public class AssemblyController {
     @Autowired
     private AssemblyValidator assemblyValidator;
 
+    /** Name of thymeleaf parameter, which will contain text in assembly form. */
+    private String assemblyTitleName = "title";
+
+    /** Title text for assembly edit form.  */
+    private String assemblyEditTitleText = "Editace - ";
+
+    /** Title text for new assembly form. */
+    private String assemblyNewTitleText = "Vytvoření nové sestavy";
+
+    /** Name of thymeleaf parameter, which will contain error text from validator. */
+    private String assemblyErrorName = "errorText";
+
     /**
      * Bind assembly validator.
      * @param binder Binder.
@@ -102,6 +114,11 @@ public class AssemblyController {
         return modelAndView;
     }
 
+    /**
+     * Get method for assembly edit.
+     * @param id - Id of assembly.
+     * @return ModelAndView for assembly edit.
+     */
     @GetMapping("/assembly_edit")
     public ModelAndView assemblyEditIndex(@Valid @ModelAttribute("assemblyID") String id) {
         ModelAndView modelAndView = new ModelAndView("assembly_manage");
@@ -111,6 +128,9 @@ public class AssemblyController {
         Assembly assembly = this.assemblyManager.getAssemblyById(Long.parseLong(id));
 
         modelMap.addAttribute("assembly", assembly);
+
+        modelMap.addAttribute(assemblyTitleName, assemblyEditTitleText + " " + assembly.getName());
+
         modelMap.addAttribute("allRoles", this.roleManager.getRoles());
         modelMap.addAttribute("allParameterTypes", this.parameterTypeManager.getParameterTypes());
         modelMap.addAttribute("allFunctions", this.functionManager.getFunctions());
@@ -120,21 +140,38 @@ public class AssemblyController {
         return modelAndView;
     }
 
+    /**
+     * Post method for assembly edit.
+     * @param assembly - Edited assembly values.
+     * @param result - Validator results.
+     * @param assemblyIsPublic - Attribute indicating if assembly is public or not.
+     * @return ModelAndView for assembly edit.
+     */
     @PostMapping("/assembly_edit")
-    public ModelAndView assemblyEditPostIndex(@Valid @ModelAttribute("assembly") Assembly assembly, @Valid @ModelAttribute("checkboxPublic") String assemblyIsPublic) {
+    public ModelAndView assemblyEditPostIndex(@Valid @ModelAttribute("assembly") Assembly assembly, BindingResult result,
+                                              @Valid @ModelAttribute("checkboxPublic") String assemblyIsPublic) {
 
         ModelAndView modelAndView = new ModelAndView("assembly_manage");
         ModelMap modelMap = modelAndView.getModelMap();
 
-        if (assemblyIsPublic.equals("on")) assembly.setIsPublic(1);
-        else assembly.setIsPublic(0);
+        if (result.hasErrors()) {
+            modelMap.addAttribute("assembly", assembly);
+
+            modelMap.addAttribute(assemblyErrorName, getFullErrorMessage(result));
+            modelMap.addAttribute(assemblyTitleName, assemblyEditTitleText + " " + assembly.getName());
+        }
+        else {
+            if (assemblyIsPublic.equals("on")) assembly.setIsPublic(1);
+            else assembly.setIsPublic(0);
 
-        // TODO: chybí defaultValue.
-        Long assemblyID = this.assemblyManager.updateAssembly(assembly);
-        this.parameterManager.updateParameters(assemblyID, assembly.getParameters());
-        Assembly updatedAssembly = this.assemblyManager.getAssemblyById(assembly.getId());
+            // TODO: chybí defaultValue.
+            Long assemblyID = this.assemblyManager.updateAssembly(assembly);
+            this.parameterManager.updateParameters(assemblyID, assembly.getParameters());
+            Assembly updatedAssembly = this.assemblyManager.getAssemblyById(assembly.getId());
 
-        modelMap.addAttribute("assembly", updatedAssembly);
+            modelMap.addAttribute("assembly", updatedAssembly);
+            modelMap.addAttribute(assemblyTitleName, assemblyEditTitleText + " " + updatedAssembly.getName());
+        }
 
         modelMap.addAttribute("allRoles", this.roleManager.getRoles());
         modelMap.addAttribute("allParameterTypes", this.parameterTypeManager.getParameterTypes());
@@ -145,6 +182,10 @@ public class AssemblyController {
         return modelAndView;
     }
 
+    /**
+     * Get method for new assembly form.
+     * @return ModelAndView for new assembly.
+     */
     @GetMapping("/assembly_new")
     public ModelAndView assemblyNewIndex() {
         ModelAndView modelAndView = new ModelAndView("assembly_manage");
@@ -152,6 +193,9 @@ public class AssemblyController {
         ModelMap modelMap = modelAndView.getModelMap();
 
         modelMap.addAttribute("assembly", new Assembly());
+
+        modelMap.addAttribute(assemblyTitleName, assemblyNewTitleText);
+
         modelMap.addAttribute("allRoles", this.roleManager.getRoles());
         modelMap.addAttribute("allParameterTypes", this.parameterTypeManager.getParameterTypes());
         modelMap.addAttribute("allFunctions", this.functionManager.getFunctions());
@@ -161,17 +205,39 @@ public class AssemblyController {
         return modelAndView;
     }
 
+    /**
+     * Post method for new assembly form.
+     * @param assembly - Assembly values.
+     * @param result - Validator results.
+     * @return
+     */
     @PostMapping("/assembly_new")
-    public ModelAndView assemblyNewIndexPost(@Valid @ModelAttribute("assembly") Assembly assembly) {
+    public ModelAndView assemblyNewIndexPost(@Valid @ModelAttribute("assembly") Assembly assembly, BindingResult result) {
         ModelAndView modelAndView = new ModelAndView();
 
+        if (result.hasErrors()) {
+            ModelMap modelMap = modelAndView.getModelMap();
+            modelAndView.setViewName("assembly_manage");
+
+            modelMap.addAttribute("assembly", assembly);
+
+            modelMap.addAttribute(assemblyTitleName, assemblyNewTitleText);
+            modelMap.addAttribute(assemblyErrorName, getFullErrorMessage(result));
+
+            modelMap.addAttribute("allRoles", this.roleManager.getRoles());
+            modelMap.addAttribute("allParameterTypes", this.parameterTypeManager.getParameterTypes());
+            modelMap.addAttribute("allFunctions", this.functionManager.getFunctions());
+            modelMap.addAttribute("allOperators", this.operatorManager.getOperators());
+            modelMap.addAttribute("allLocations", this.locationManager.getLocations());
+        }
+
         ModelMap modelMap = modelAndView.getModelMap();
 
         // TODO: chybí získání default value.
         Long assemblyId = this.assemblyManager.createAssembly(assembly);
         this.parameterManager.addParameters(assemblyId, assembly.getParameters());
 
-        modelAndView.setViewName("redirect:/assembly_edit?assemblyID=" + assembly.getId());
+        modelAndView.setViewName("redirect:/assembly_edit?assemblyID=" + assemblyId);
 
         return modelAndView;
     }
diff --git a/src/main/java/vldc/aswi/web/controller/BasicController.java b/src/main/java/vldc/aswi/web/controller/BasicController.java
new file mode 100644
index 00000000..c47c22c4
--- /dev/null
+++ b/src/main/java/vldc/aswi/web/controller/BasicController.java
@@ -0,0 +1,36 @@
+package vldc.aswi.web.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
+
+@Controller
+public class BasicController {
+
+    /**
+     * Return message containings all errors from validator.
+     * @param result Validator results.
+     * @return String message containings all errors from validator.
+     */
+    protected String getFullErrorMessage(BindingResult result) {
+        String message = "";
+
+        for (Object object : result.getAllErrors()) {
+            if (object instanceof FieldError) {
+                FieldError fieldError = (FieldError) object;
+
+                message += fieldError.getDefaultMessage() + ", ";
+                continue;
+            }
+
+            if (object instanceof ObjectError) {
+                ObjectError objectError = (ObjectError) object;
+
+                message += objectError.getDefaultMessage()  + ", ";
+                continue;
+            }
+        }
+        return message;
+    }
+}
diff --git a/src/main/java/vldc/aswi/web/controller/IndexController.java b/src/main/java/vldc/aswi/web/controller/IndexController.java
index 7beed8e9..1c0ee1b7 100644
--- a/src/main/java/vldc/aswi/web/controller/IndexController.java
+++ b/src/main/java/vldc/aswi/web/controller/IndexController.java
@@ -23,7 +23,7 @@ import java.util.List;
 import java.util.Map;
 
 @Controller
-public class IndexController {
+public class IndexController extends BasicController {
 
 	@Autowired
 	private SqlQueryManager sqlQueryManager;
diff --git a/src/main/java/vldc/aswi/web/controller/LoginController.java b/src/main/java/vldc/aswi/web/controller/LoginController.java
index 5c1936a9..27d2983b 100644
--- a/src/main/java/vldc/aswi/web/controller/LoginController.java
+++ b/src/main/java/vldc/aswi/web/controller/LoginController.java
@@ -4,7 +4,7 @@ import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;
 
 @Controller
-public class LoginController {
+public class LoginController extends BasicController {
 
 	@GetMapping("/login")
 	public String login() {
diff --git a/src/main/webapp/WEB-INF/templates/assembly_manage.html b/src/main/webapp/WEB-INF/templates/assembly_manage.html
index c57e179b..b799788b 100644
--- a/src/main/webapp/WEB-INF/templates/assembly_manage.html
+++ b/src/main/webapp/WEB-INF/templates/assembly_manage.html
@@ -25,11 +25,12 @@
 
   <div class="container">
     <a href="/" class="btn btn-success mb-2">Zpět</a>
-    <h1>
-      Editace - Studenti - studijní programy
+    <h1 th:text="${title}">
     </h1>
   </div>
 
+  <p th:text="${errorText}"></p>
+
 <form th:object="${assembly}" th:action="${assembly.id} != null ? @{/assembly_edit(assemblyID=${assembly.id})} : @{/assembly_new}" method="post">
   <input type="hidden" th:field="*{id}" th:value="*{id}" />
   <div class="container box">
