commit a00992e0555cc914853147f6810094276fabae67
Author: Ondřej Váně <onry.vane@gmail.com>
Date:   Sun Aug 15 23:05:14 2021 +0200

    #5 Implement user input checks
    
    - added new data types for checking input parameters
    - chenged UI for showing error messages
    - refactor validation of input parameters

diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/controller/AppController.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/controller/AppController.java
index 1fd98be..8cf3ac2 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/controller/AppController.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/controller/AppController.java
@@ -137,14 +137,17 @@ public class AppController {
                                     @RequestParam(value = "configValues", required = false) String[] configValues,
                                     @RequestParam(value = "configNames", required = false) String[] configNames) {
 
-        if (antiPatternService.saveNewConfiguration(configNames, configValues)) {
+        List<AntiPattern> antiPatterns = antiPatternService.antiPatternsToModel(antiPatternService.getAllAntiPatterns());
+        List<String> wrongParameters = antiPatternService.saveNewConfiguration(configNames, configValues);
+
+        if (wrongParameters.isEmpty()) {
             model.addAttribute("successMessage", "All configuration values has been successfully saved.");
         } else {
-            model.addAttribute("errorMessage", "One or more configuration values are not in correct format");
+            model.addAttribute("errorMessage", "One or more configuration values are not in correct format, see messages below.");
+            antiPatternService.setErrorMessages(antiPatterns, wrongParameters);
         }
 
-        model.addAttribute("antiPatterns", antiPatternService.antiPatternsToModel(antiPatternService.getAllAntiPatterns()));
-
+        model.addAttribute("antiPatterns", antiPatterns);
         return "configuration";
     }
 
@@ -155,13 +158,17 @@ public class AppController {
                                    @RequestParam(value = "configNames", required = false) String[] configNames,
                                    RedirectAttributes redirectAttrs) {
 
-        if (antiPatternService.saveNewConfiguration(configNames, configValues)) {
+        AntiPattern antiPattern = antiPatternService.antiPatternToModel(antiPatternService.getAntiPatternById(id));
+        List<String> wrongParameters = antiPatternService.saveNewConfiguration(configNames, configValues);
+
+        if (wrongParameters.isEmpty()) {
             redirectAttrs.addFlashAttribute("successMessage", "All threshold values has been successfully saved.");
         } else {
-            redirectAttrs.addFlashAttribute("errorMessage", "One or more configuration values are not in correct format");
+            redirectAttrs.addFlashAttribute("errorMessage", "One or more configuration values are not in correct format, see messages below.");
+            antiPatternService.setErrorMessages(antiPattern, wrongParameters);
         }
 
-        model.addAttribute("antiPatterns", antiPatternService.antiPatternToModel(antiPatternService.getAntiPatternById(id)));
+        model.addAttribute("antiPatterns", antiPattern);
 
         return "redirect:/anti-patterns/{id}";
     }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java
index bf05dfb..058103b 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java
@@ -3,6 +3,7 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
 import cz.zcu.fav.kiv.antipatterndetectionapp.Constants;
 import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.*;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.Percentage;
 import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -19,12 +20,15 @@ public class BusinessAsUsualDetectorImpl implements AntiPatternDetector {
             "Absence of a retrospective after individual " +
                     "iterations or after the completion project.",
             new HashMap<>() {{
-                put("divisionOfIterationsWithRetrospective", new Configuration<Float>("divisionOfIterationsWithRetrospective",
+                put("divisionOfIterationsWithRetrospective", new Configuration<Percentage>("divisionOfIterationsWithRetrospective",
                         "Division of iterations with retrospective",
-                        "Minimum percentage of the total number of iterations with a retrospective (0,1)", 0.66666f));
+                        "Minimum percentage of the total number of iterations with a retrospective (0,100)",
+                        "Percentage must be between 0 and 100",
+                        new Percentage(66)));
                 put("searchSubstringsWithRetrospective", new Configuration<String>("searchSubstringsWithRetrospective",
                         "Search substrings with retrospective",
                         "Substring that will be search in wikipages and activities",
+                        "Maximum number of substrings is ten and must not starts and ends with characters || ",
                         "%retr%" + Constants.SUBSTRING_DELIMITER +
                                 "%revi%" + Constants.SUBSTRING_DELIMITER +
                                 "%week%scrum%"));
@@ -37,7 +41,7 @@ public class BusinessAsUsualDetectorImpl implements AntiPatternDetector {
     private List<String> sqlQueries;
     
     private float getDivisionOfIterationsWithRetrospective() {
-        return (float) antiPattern.getConfigurations().get("divisionOfIterationsWithRetrospective").getValue();
+        return ((Percentage) antiPattern.getConfigurations().get("divisionOfIterationsWithRetrospective").getValue()).getValue();
     }
 
     private List<String> getSearchSubstringsWithRetrospective() {
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/LongOrNonExistentFeedbackLoopsDetectorImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/LongOrNonExistentFeedbackLoopsDetectorImpl.java
index d7affbe..daab69d 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/LongOrNonExistentFeedbackLoopsDetectorImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/LongOrNonExistentFeedbackLoopsDetectorImpl.java
@@ -3,6 +3,8 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
 import cz.zcu.fav.kiv.antipatterndetectionapp.Constants;
 import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.*;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.Percentage;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveFloat;
 import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -24,16 +26,21 @@ public class LongOrNonExistentFeedbackLoopsDetectorImpl implements AntiPatternDe
                     "some misunderstood functionality can be created and we have to spend " +
                     "a lot of effort and time to redo it. ",
             new HashMap<>() {{
-                put("divisionOfIterationsWithFeedbackLoop", new Configuration<Float>("divisionOfIterationsWithFeedbackLoop",
+                put("divisionOfIterationsWithFeedbackLoop", new Configuration<Percentage>("divisionOfIterationsWithFeedbackLoop",
                         "Division of iterations with feedback loop",
-                        "Minimum percentage of the total number of iterations with feedback loop (0,1)", 0.5f));
-                put("maxGapBetweenFeedbackLoopRate", new Configuration<Float>("maxGapBetweenFeedbackLoopRate",
+                        "Minimum percentage of the total number of iterations with feedback loop (0,1)",
+                        "Percentage must be between 0 and 100",
+                        new Percentage(50)));
+                put("maxGapBetweenFeedbackLoopRate", new Configuration<PositiveFloat>("maxGapBetweenFeedbackLoopRate",
                         "Maximum gap between feedback loop rate",
                         "Value that multiplies average iteration length for given project. Result" +
-                                " is maximum threshold value for gap between feedback loops in days.", 2f));
+                                " is maximum threshold value for gap between feedback loops in days.",
+                        "Maximum gap between feedback loop rate must be positive float number",
+                        new PositiveFloat(2f)));
                 put("searchSubstringsWithFeedbackLoop", new Configuration<String>("searchSubstringsWithFeedbackLoop",
                         "Search substrings with feedback loop",
                         "Substring that will be search in wikipages and activities",
+                        "Maximum number of substrings is ten and must not starts and ends with characters ||",
                         "%schůz%zákazník%" + Constants.SUBSTRING_DELIMITER +
                                 "%předvedení%zákazník%" + Constants.SUBSTRING_DELIMITER +
                                 "%zákazn%demo%" + Constants.SUBSTRING_DELIMITER +
@@ -49,7 +56,7 @@ public class LongOrNonExistentFeedbackLoopsDetectorImpl implements AntiPatternDe
     private List<String> sqlQueries;
 
     private float getDivisionOfIterationsWithFeedbackLoop() {
-        return (float) antiPattern.getConfigurations().get("divisionOfIterationsWithFeedbackLoop").getValue();
+        return ((Percentage) antiPattern.getConfigurations().get("divisionOfIterationsWithFeedbackLoop").getValue()).getValue();
     }
 
     private float getMaxGapBetweenFeedbackLoopRate() {
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/NinetyNinetyRuleDetectorImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/NinetyNinetyRuleDetectorImpl.java
index 8efdeaf..01f8afa 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/NinetyNinetyRuleDetectorImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/NinetyNinetyRuleDetectorImpl.java
@@ -2,6 +2,8 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
 
 import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.*;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveFloat;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveInteger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -20,12 +22,16 @@ public class NinetyNinetyRuleDetectorImpl implements AntiPatternDetector {
                     "The functionality is almost done, some number is already closed and is only waiting " +
                     "for one activity to close, but it has been open for a long time.",
             new HashMap<>() {{
-                put("maxDivisionRange", new Configuration<Double>("maxDivisionRange",
+                put("maxDivisionRange", new Configuration<PositiveFloat>("maxDivisionRange",
                         "Maximum ration value",
-                        "Maximum ratio value of spent and estimated time", 1.25));
-                put("maxBadDivisionLimit", new Configuration<Integer>("maxBadDivisionLimit",
+                        "Maximum ratio value of spent and estimated time",
+                        "Ration values must be positive float number",
+                        new PositiveFloat(1.25f)));
+                put("maxBadDivisionLimit", new Configuration<PositiveInteger>("maxBadDivisionLimit",
                         "Maximum iterations thresholds",
-                        "Maximum number of consecutive iterations where the thresholds were exceeded", 2));
+                        "Maximum number of consecutive iterations where the thresholds were exceeded",
+                        "Maximum number of consecutive iterations must be positive integer number",
+                        new PositiveInteger(2)));
             }});
 
     private final String sqlFileName = "ninety_ninety_rule.sql";
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/RoadToNowhereDetectorImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/RoadToNowhereDetectorImpl.java
index f10fd31..9874e80 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/RoadToNowhereDetectorImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/RoadToNowhereDetectorImpl.java
@@ -3,6 +3,7 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
 import cz.zcu.fav.kiv.antipatterndetectionapp.Constants;
 import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.*;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveInteger;
 import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -22,15 +23,20 @@ public class RoadToNowhereDetectorImpl implements AntiPatternDetector {
                     "takes place on an ad hoc basis with an uncertain " +
                     "outcome and deadline. There is no project plan in the project.",
             new HashMap<>() {{
-                put("minNumberOfWikiPagesWithProjectPlan", new Configuration<Integer>("minNumberOfWikiPagesWithProjectPlan",
+                put("minNumberOfWikiPagesWithProjectPlan", new Configuration<PositiveInteger>("minNumberOfWikiPagesWithProjectPlan",
                         "Minimum number of wiki pages with project plan",
-                        "Number of wiki pages", 1));
-                put("minNumberOfActivitiesWithProjectPlan", new Configuration<Integer>("minNumberOfActivitiesWithProjectPlan",
+                        "Number of wiki pages",
+                        "Minimum number of wikipages must be positive integer number",
+                        new PositiveInteger(1)));
+                put("minNumberOfActivitiesWithProjectPlan", new Configuration<PositiveInteger>("minNumberOfActivitiesWithProjectPlan",
                         "Minimum number of activities with project plan",
-                        "Number of activities", 1));
+                        "Number of activities",
+                        "Minimum number of activities must be positive integer number",
+                        new PositiveInteger(1)));
                 put("searchSubstringsWithProjectPlan", new Configuration<String>("searchSubstringsWithProjectPlan",
                         "Search substrings with project plan",
                         "Substring that will be search in wikipages and activities",
+                        "Maximum number of substrings is ten and must not starts and ends with characters ||",
                         "%plán projektu%" + Constants.SUBSTRING_DELIMITER +
                                 "%project plan%" + Constants.SUBSTRING_DELIMITER +
                                 "%plan project%" + Constants.SUBSTRING_DELIMITER +
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/SpecifyNothingDetectorImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/SpecifyNothingDetectorImpl.java
index 6e68e1e..9ac9c85 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/SpecifyNothingDetectorImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/SpecifyNothingDetectorImpl.java
@@ -3,6 +3,7 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
 import cz.zcu.fav.kiv.antipatterndetectionapp.Constants;
 import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.*;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveInteger;
 import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,18 +22,25 @@ public class SpecifyNothingDetectorImpl implements AntiPatternDetector {
             "The specification is not done intentionally. Programmers are " +
                     "expected to work better without written specifications.",
             new HashMap<>() {{
-                put("minNumberOfWikiPagesWithSpecification", new Configuration<Integer>("minNumberOfWikiPagesWithSpecification",
+                put("minNumberOfWikiPagesWithSpecification", new Configuration<PositiveInteger>("minNumberOfWikiPagesWithSpecification",
                         "Minimum number of wiki pages with project specification",
-                        "Number of wiki pages", 1));
-                put("minNumberOfActivitiesWithSpecification", new Configuration<Integer>("minNumberOfActivitiesWithSpecification",
+                        "Number of wiki pages",
+                        "Minimum number of wikipages must be positive integer number",
+                        new PositiveInteger(1)));
+                put("minNumberOfActivitiesWithSpecification", new Configuration<PositiveInteger>("minNumberOfActivitiesWithSpecification",
                         "Minimum number of activities with project specification",
-                        "Number of activities", 1));
-                put("minAvgLengthOfActivityDescription", new Configuration<Integer>("minAvgLengthOfActivityDescription",
+                        "Number of activities",
+                        "Minimum number of activities with project specification must be positive integer number",
+                        new PositiveInteger(1)));
+                put("minAvgLengthOfActivityDescription", new Configuration<PositiveInteger>("minAvgLengthOfActivityDescription",
                         "Minimum average length of activity description",
-                        "Minimum average number of character of activity description", 150));
+                        "Minimum average number of character of activity description",
+                        "Minimum average length of activity description must be positive integer number",
+                        new PositiveInteger(150)));
                 put("searchSubstringsWithProjectSpecification", new Configuration<String>("searchSubstringsWithProjectSpecification",
                         "Search substrings with project specification",
                         "Substring that will be search in wikipages and activities",
+                        "Maximum number of substrings is ten and must not starts and ends with characters ||",
                         "%dsp%" + Constants.SUBSTRING_DELIMITER +
                                 "%specifikace%" + Constants.SUBSTRING_DELIMITER +
                                 "%specification%" + Constants.SUBSTRING_DELIMITER +
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/TooLongSprintDetectorImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/TooLongSprintDetectorImpl.java
index be54f4a..1b01069 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/TooLongSprintDetectorImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/TooLongSprintDetectorImpl.java
@@ -2,6 +2,7 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
 
 import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.*;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveInteger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -23,12 +24,16 @@ public class TooLongSprintDetectorImpl implements AntiPatternDetector {
                     "beginning and at the end of the project, but it should not " +
                     "change in the already started project).",
             new HashMap<>() {{
-                put("maxIterationLength", new Configuration<Integer>("maxIterationLength",
+                put("maxIterationLength", new Configuration<PositiveInteger>("maxIterationLength",
                         "Max Iteration Length",
-                        "Maximum iteration length in days", 21));
-                put("maxNumberOfTooLongIterations", new Configuration<Integer>("maxNumberOfTooLongIterations",
+                        "Maximum iteration length in days",
+                        "Max iteration length must be positive integer",
+                        new PositiveInteger(21)));
+                put("maxNumberOfTooLongIterations", new Configuration<PositiveInteger>("maxNumberOfTooLongIterations",
                         "Max number of foo long iterations",
-                        "Maximum number of too long iterations in project", 0));
+                        "Maximum number of too long iterations in project",
+                        "Max number of too long iterations must be positive integer",
+                        new PositiveInteger(0)));
             }}
     );
 
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/VaryingSprintLengthDetectorImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/VaryingSprintLengthDetectorImpl.java
index eb0f804..827e1ae 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/VaryingSprintLengthDetectorImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/VaryingSprintLengthDetectorImpl.java
@@ -2,6 +2,7 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
 
 import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.*;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveInteger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -24,12 +25,16 @@ public class VaryingSprintLengthDetectorImpl implements AntiPatternDetector {
                     "but the length of the sprint should not change " +
                     "during the project.",
             new HashMap<>() {{
-                put("maxDaysDifference", new Configuration<Integer>("maxDaysDifference",
+                put("maxDaysDifference", new Configuration<PositiveInteger>("maxDaysDifference",
                         "Max days difference",
-                        "Maximum distance of two consecutive iterations in days", 7));
-                put("maxIterationChanged", new Configuration<Integer>("maxIterationChanged",
+                        "Maximum distance of two consecutive iterations in days",
+                        "Maximum distance must be positive integer number",
+                        new PositiveInteger(7)));
+                put("maxIterationChanged", new Configuration<PositiveInteger>("maxIterationChanged",
                         "Max number of iteration changed",
-                        "Maximum allowed number of significant changes in iteration lengths", 1));
+                        "Maximum allowed number of significant changes in iteration lengths",
+                        "Maximum number of iterations changed must be positive integer number",
+                        new PositiveInteger(1)));
             }});
 
     private final String sqlFileName = "varying_sprint_length.sql";
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/Configuration.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/Configuration.java
index e4f835b..3fb8c19 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/Configuration.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/Configuration.java
@@ -4,6 +4,8 @@ public class Configuration<T> {
     private String name;
     private String printName;
     private String description;
+    private String errorMessage;
+    private boolean isErrorMessageShown;
     private T value;
 
     public Configuration(String name, String printName, String description, T value) {
@@ -11,6 +13,16 @@ public class Configuration<T> {
         this.printName = printName;
         this.description = description;
         this.value = value;
+        this.isErrorMessageShown = false;
+    }
+
+    public Configuration(String name, String printName, String description, String errorMessage, T value) {
+        this.name = name;
+        this.printName = printName;
+        this.description = description;
+        this.errorMessage = errorMessage;
+        this.value = value;
+        this.isErrorMessageShown = false;
     }
 
     public String getName() {
@@ -44,4 +56,20 @@ public class Configuration<T> {
     public void setValue(T value) {
         this.value = value;
     }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+
+    public boolean isErrorMessageShown() {
+        return isErrorMessageShown;
+    }
+
+    public void setErrorMessageShown(boolean errorMessageShown) {
+        isErrorMessageShown = errorMessageShown;
+    }
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/Percentage.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/Percentage.java
new file mode 100644
index 0000000..2d44cf0
--- /dev/null
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/Percentage.java
@@ -0,0 +1,49 @@
+package cz.zcu.fav.kiv.antipatterndetectionapp.model.types;
+
+public class Percentage extends Number {
+
+    public static final float MAX_VALUE = 100;
+    public static final float MIN_VALUE = 0;
+
+    private final int value;
+
+    public Percentage(int value) throws NumberFormatException {
+        if (value > MAX_VALUE || value < MIN_VALUE) {
+            throw new NumberFormatException("Percentage should be between 0 and 100");
+        }
+        this.value = value;
+    }
+
+    public static Percentage parsePercentage(String value) {
+        return new Percentage(Integer.parseInt(value));
+    }
+
+    @Override
+    public int intValue() {
+        return this.value;
+    }
+
+    @Override
+    public long longValue() {
+        return (long) this.value;
+    }
+
+    @Override
+    public float floatValue() {
+        return (float) this.value;
+    }
+
+    @Override
+    public double doubleValue() {
+        return (double) this.value;
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+
+    public float getValue() {
+        return (float) value/100;
+    }
+}
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/PositiveFloat.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/PositiveFloat.java
new file mode 100644
index 0000000..2d59429
--- /dev/null
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/PositiveFloat.java
@@ -0,0 +1,45 @@
+package cz.zcu.fav.kiv.antipatterndetectionapp.model.types;
+
+public class PositiveFloat extends Number {
+    public static final float MAX_VALUE = Float.MAX_VALUE;
+    public static final int MIN_VALUE = 0;
+
+    private final float value;
+
+    public PositiveFloat(float value) throws NumberFormatException {
+        if (value > MAX_VALUE || value < MIN_VALUE) {
+            throw new NumberFormatException("Positive integer should be between 0 and infinity");
+        }
+        this.value = value;
+    }
+
+
+    @Override
+    public int intValue() {
+        return 0;
+    }
+
+    @Override
+    public long longValue() {
+        return 0;
+    }
+
+    @Override
+    public float floatValue() {
+        return 0;
+    }
+
+    @Override
+    public double doubleValue() {
+        return 0;
+    }
+
+    public static PositiveFloat parsePositiveFloat(String value) {
+        return new PositiveFloat(Float.parseFloat(value));
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+}
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/PositiveInteger.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/PositiveInteger.java
new file mode 100644
index 0000000..e367a5e
--- /dev/null
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/types/PositiveInteger.java
@@ -0,0 +1,46 @@
+package cz.zcu.fav.kiv.antipatterndetectionapp.model.types;
+
+public class PositiveInteger extends Number {
+
+    public static final int MAX_VALUE = Integer.MAX_VALUE;
+    public static final int MIN_VALUE = 0;
+
+    private final int value;
+
+    public PositiveInteger(int value) throws NumberFormatException {
+        if (value > MAX_VALUE || value < MIN_VALUE) {
+            throw new NumberFormatException("Positive integer should be between 0 and infinity");
+        }
+        this.value = value;
+    }
+
+
+    @Override
+    public int intValue() {
+        return 0;
+    }
+
+    @Override
+    public long longValue() {
+        return 0;
+    }
+
+    @Override
+    public float floatValue() {
+        return 0;
+    }
+
+    @Override
+    public double doubleValue() {
+        return 0;
+    }
+
+    public static PositiveInteger parsePositiveInteger(String value) {
+        return new PositiveInteger(Integer.parseInt(value));
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+}
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternService.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternService.java
index 301ed8e..d126021 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternService.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternService.java
@@ -18,7 +18,7 @@ public interface AntiPatternService {
 
     List<AntiPatternDetector> getAllAntiPatternsForGivenIds(Long[] ids);
 
-    boolean saveNewConfiguration(String[] configNames, String[] configValues);
+    List<String> saveNewConfiguration(String[] configNames, String[] configValues);
 
     void saveAnalyzedProjects(String[] selectedProjects, String[] selectedAntiPatterns);
 
@@ -34,4 +34,7 @@ public interface AntiPatternService {
 
     List<QueryResult> getResults();
 
+    List<AntiPattern> setErrorMessages(List<AntiPattern> antiPatterns, List<String> wrongParameters);
+
+    AntiPattern setErrorMessages(AntiPattern antiPattern, List<String> wrongParameters);
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternServiceImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternServiceImpl.java
index 6bffc38..f71363d 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternServiceImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternServiceImpl.java
@@ -3,14 +3,20 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.service;
 import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors.AntiPatternDetector;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.AntiPattern;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.CacheablesValues;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.Configuration;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.QueryResult;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.Percentage;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveFloat;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.types.PositiveInteger;
 import cz.zcu.fav.kiv.antipatterndetectionapp.repository.AntiPatternRepository;
+import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 
 @Service
@@ -36,14 +42,25 @@ public class AntiPatternServiceImpl implements AntiPatternService {
     public List<AntiPattern> antiPatternsToModel(List<AntiPatternDetector> antiPatternDetectors) {
         List<AntiPattern> antiPatterns = new LinkedList<>();
         for (AntiPatternDetector antiPatternDetector : antiPatternDetectors) {
-            antiPatterns.add(antiPatternDetector.getAntiPatternModel());
+            AntiPattern antiPattern = antiPatternDetector.getAntiPatternModel();
+
+            // set to default value
+            for (Map.Entry<String, Configuration> config : antiPattern.getConfigurations().entrySet()) {
+                config.getValue().setErrorMessageShown(false);
+            }
+            antiPatterns.add(antiPattern);
         }
         return antiPatterns;
     }
 
     @Override
     public AntiPattern antiPatternToModel(AntiPatternDetector antiPatternDetector) {
-        return antiPatternDetector.getAntiPatternModel();
+        AntiPattern antiPattern = antiPatternDetector.getAntiPatternModel();
+        // set to default value
+        for (Map.Entry<String, Configuration> config : antiPattern.getConfigurations().entrySet()) {
+            config.getValue().setErrorMessageShown(false);
+        }
+        return antiPattern;
     }
 
     @Override
@@ -56,9 +73,9 @@ public class AntiPatternServiceImpl implements AntiPatternService {
         return antiPatternDetectors;
     }
 
-    @Override
-    public boolean saveNewConfiguration(String[] configNames, String[] configValues) {
+    public List<String> saveNewConfiguration(String[] configNames, String[] configValues) {
         List<AntiPatternDetector> antiPatternDetectors = antiPatternRepository.getAllAntiPatterns();
+        List<String> incorrectParameters = new ArrayList<>();
 
         for (AntiPatternDetector antiPatternDetector : antiPatternDetectors) {
             // not every anti-pattern should have configuration
@@ -73,31 +90,57 @@ public class AntiPatternServiceImpl implements AntiPatternService {
                             antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).setValue((Integer.parseInt(configValues[i])));
                             setConfigurationChanged(true);
                         } catch (NumberFormatException e) {
-                            return false;
+                            incorrectParameters.add(configNames[i]);
                         }
 
+                    } else if (antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).getValue().getClass() == Percentage.class) {
+                        try {
+                            antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).setValue((Percentage.parsePercentage(configValues[i])));
+                            setConfigurationChanged(true);
+                        } catch (NumberFormatException e) {
+                            incorrectParameters.add(configNames[i]);
+                        }
+                    } else if (antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).getValue().getClass() == PositiveInteger.class) {
+                        try {
+                            antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).setValue((PositiveInteger.parsePositiveInteger(configValues[i])));
+                            setConfigurationChanged(true);
+                        } catch (NumberFormatException e) {
+                            incorrectParameters.add(configNames[i]);
+                        }
+                    } else if (antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).getValue().getClass() == PositiveFloat.class) {
+                        try {
+                            antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).setValue((PositiveFloat.parsePositiveFloat(configValues[i])));
+                            setConfigurationChanged(true);
+                        } catch (NumberFormatException e) {
+                            incorrectParameters.add(configNames[i]);
+                        }
                     } else if (antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).getValue().getClass() == Float.class) {
                         try {
                             antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).setValue((Float.parseFloat(configValues[i])));
                             setConfigurationChanged(true);
                         } catch (NumberFormatException e) {
-                            return false;
+                            incorrectParameters.add(configNames[i]);
                         }
                     } else if (antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).getValue().getClass() == Double.class) {
                         try {
                             antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).setValue((Double.parseDouble(configValues[i])));
                             setConfigurationChanged(true);
                         } catch (NumberFormatException e) {
-                            return false;
+                            incorrectParameters.add(configNames[i]);
                         }
                     } else if (antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).getValue().getClass() == String.class) {
+                        if (Utils.checkStringSubstrings(configValues[i])) {
                             antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).setValue((configValues[i]));
                             setConfigurationChanged(true);
+
+                        } else {
+                            incorrectParameters.add(configNames[i]);
+                        }
                     }
                 }
             }
         }
-        return true;
+        return incorrectParameters;
     }
 
     @Override
@@ -117,13 +160,13 @@ public class AntiPatternServiceImpl implements AntiPatternService {
     }
 
     @Override
-    public void setConfigurationChanged(boolean configurationChanged) {
-        this.cacheablesValues.setConfigurationChanged(configurationChanged);
+    public boolean isConfigurationChanged() {
+        return this.cacheablesValues.isConfigurationChanged();
     }
 
     @Override
-    public boolean isConfigurationChanged() {
-        return this.cacheablesValues.isConfigurationChanged();
+    public void setConfigurationChanged(boolean configurationChanged) {
+        this.cacheablesValues.setConfigurationChanged(configurationChanged);
     }
 
     @Override
@@ -135,4 +178,25 @@ public class AntiPatternServiceImpl implements AntiPatternService {
     public List<QueryResult> getResults() {
         return this.cacheablesValues.getResults();
     }
+
+    @Override
+    public List<AntiPattern> setErrorMessages(List<AntiPattern> antiPatterns, List<String> wrongParameters) {
+        for (AntiPattern antiPattern : antiPatterns) {
+            for (Map.Entry<String, Configuration> config : antiPattern.getConfigurations().entrySet()) {
+                if (wrongParameters.contains(config.getKey())) {
+                    config.getValue().setErrorMessageShown(true);
+                } else {
+                    config.getValue().setErrorMessageShown(false);
+                }
+            }
+        }
+        return antiPatterns;
+    }
+
+    @Override
+    public AntiPattern setErrorMessages(AntiPattern antiPattern, List<String> wrongParameters) {
+        List<AntiPattern> antiPatterns = new ArrayList<>();
+        antiPatterns.add(antiPattern);
+        return setErrorMessages(antiPatterns, wrongParameters).get(0);
+    }
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/utils/Utils.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/utils/Utils.java
index 77691ba..9e67075 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/utils/Utils.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/utils/Utils.java
@@ -1,5 +1,6 @@
 package cz.zcu.fav.kiv.antipatterndetectionapp.utils;
 
+import cz.zcu.fav.kiv.antipatterndetectionapp.Constants;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.ResultDetail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -73,4 +74,20 @@ public class Utils {
 
         return preparedQueries;
     }
+
+    public static boolean checkStringSubstrings(String substrings) {
+        if (substrings.startsWith(Constants.SUBSTRING_DELIMITER)) {
+            return false;
+        }
+
+        if (substrings.endsWith(Constants.SUBSTRING_DELIMITER)) {
+            return false;
+        }
+
+        if (substrings.split("\\|\\|").length > 10) {
+            return false;
+        }
+
+        return true;
+    }
 }
diff --git a/src/main/webapp/WEB-INF/templates/anti-pattern.html b/src/main/webapp/WEB-INF/templates/anti-pattern.html
index 09e29f1..9c5d6c7 100644
--- a/src/main/webapp/WEB-INF/templates/anti-pattern.html
+++ b/src/main/webapp/WEB-INF/templates/anti-pattern.html
@@ -91,12 +91,17 @@
             <div th:each="config : ${antiPattern.configurations}" class="form-group">
                 <label th:text="${config.value.printName} + ':'"
                        th:for="${config.value.name}"></label>
-                <input th:value="${config.value.value}" class="form-control" th:id="${config.value.name}"
+                <small th:text="${config.value.description}" th:value="${config.value.name}"
+                       class="form-text text-muted"></small>
+                <input th:if="${config.value.isErrorMessageShown}" th:value="${config.value.value}" class="form-control is-invalid" th:id="${config.value.name}"
+                       name="configValues">
+                <input th:unless="${config.value.isErrorMessageShown}" th:value="${config.value.value}" class="form-control" th:id="${config.value.name}"
                        name="configValues">
                 <input th:value="${config.value.name}" style="display: none" class="form-control"
                        name="configNames">
-                <small th:text="${config.value.description}" th:value="${config.value.name}"
-                       class="form-text text-muted"></small>
+                <small th:if="${config.value.isErrorMessageShown}" th:text="${config.value.errorMessage}" th:value="${config.value.errorMessage}"
+                       class="form-text text-danger"></small>
+
             </div>
             <div class="analyze-button-container">
                 <button type="submit" class="btn btn-primary btn-lg btn-block">Save configuration</button>
diff --git a/src/main/webapp/WEB-INF/templates/configuration.html b/src/main/webapp/WEB-INF/templates/configuration.html
index 39c28a2..02c32b6 100644
--- a/src/main/webapp/WEB-INF/templates/configuration.html
+++ b/src/main/webapp/WEB-INF/templates/configuration.html
@@ -52,12 +52,17 @@
                     <label th:text="${config.value.printName} + ':'" th:for="${config.value.name}"
                            class="col-sm-5 col-form-label"></label>
                     <div class="col-sm-5">
-                        <input th:value="${config.value.value}" class="form-control" th:id="${config.value.name}"
+                        <small th:text="${config.value.description}" th:value="${config.value.name}"
+                               class="form-text text-muted"></small>
+                        <input th:if="${config.value.isErrorMessageShown}" th:value="${config.value.value}" class="form-control is-invalid" th:id="${config.value.name}"
+                               name="configValues">
+                        <input th:unless="${config.value.isErrorMessageShown}" th:value="${config.value.value}" class="form-control" th:id="${config.value.name}"
                                name="configValues">
                         <input th:value="${config.value.name}" style="display: none" class="form-control"
                                name="configNames">
-                        <small th:text="${config.value.description}" th:value="${config.value.name}"
-                               class="form-text text-muted"></small>
+                        <small th:if="${config.value.isErrorMessageShown}" th:text="${config.value.errorMessage}" th:value="${config.value.errorMessage}"
+                               class="form-text text-danger"></small>
+
                     </div>
                 </div>
 
