commit 927147ab7c5621f91497ae34efe98753deb94c85
Author: stepanekp <1stepanekpetr@gmail.com>
Date:   Thu Mar 31 21:06:14 2022 +0200

    #14 Configuration repository and service functions added

diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/repository/ConfigurationRepository.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/repository/ConfigurationRepository.java
new file mode 100644
index 0000000..a80e8bd
--- /dev/null
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/repository/ConfigurationRepository.java
@@ -0,0 +1,94 @@
+package cz.zcu.fav.kiv.antipatterndetectionapp.repository;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import cz.zcu.fav.kiv.antipatterndetectionapp.utils.JsonParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.ServletContextAware;
+
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.net.URL;
+import java.nio.file.Files;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class ConfigurationRepository implements ServletContextAware {
+
+    private ServletContext servletContext;
+    private static final String CONFIGURATION_DIR = "/configurations/";
+    private final Logger LOGGER = LoggerFactory.getLogger(ConfigurationRepository.class);
+
+    // Map<ConfigurationName, Map<AntiPatternName, Map<ThresholdName, value>>>
+    public Map<String, Map<String, Map<String, String>>> allConfigurations;
+
+    @Override
+    public void setServletContext(ServletContext servletContext) {
+        this.servletContext = servletContext;
+
+        loadConfigurations();
+    }
+
+    private void loadConfigurations(){
+        LOGGER.info("-------START READING CONFIGURATIONS FROM FILES-------");
+        Map<String, Map<String, Map<String, String>>> configurations = new HashMap<>();
+        try {
+            URL url = servletContext.getResource(CONFIGURATION_DIR);
+            File folder = new File(url.getFile());
+
+
+
+            for (final File fileEntry : folder.listFiles()) {
+                LOGGER.info("Reading configuration from file " + fileEntry.getName());
+                String json = "";   // json configuration file
+                JsonNode node = null;
+                try {
+                    json = new String(Files.readAllBytes(fileEntry.toPath()));
+                    node = JsonParser.parse(json);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+
+
+                String configurationName = fileEntry.getName().split("\\.")[0];
+
+                JsonNode arrayOfConfigurations = node.get("configuration");
+
+                Map<String, Map<String, String>> newAntiPatternMap = new HashMap<>();
+
+                for (int i = 0; i < arrayOfConfigurations.size(); i++) {
+
+                    JsonNode antiPatternNode = arrayOfConfigurations.get(i);
+
+                    String antiPatternName = antiPatternNode.get("antiPattern").asText();
+
+                    JsonNode arrayOfThresholds = antiPatternNode.get("thresholds");
+
+                    Map<String, String> newThresholds = new HashMap<String, String>();
+
+                    for (int j = 0; j < arrayOfThresholds.size(); j++) {
+                        JsonNode thresholdNode = arrayOfThresholds.get(j);
+
+                        String thresholdName = thresholdNode.get("thresholdName").asText();
+                        String value = thresholdNode.get("value").asText();
+
+
+                        newThresholds.put(thresholdName, value);
+                    }
+
+                    newAntiPatternMap.put(antiPatternName, newThresholds);
+
+                }
+                configurations.put(configurationName, newAntiPatternMap);
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        this.allConfigurations = configurations;
+
+        LOGGER.info("-------FINISHED READING CONFIGURATIONS FROM FILES-------");
+    }
+}
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/ConfigurationService.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/ConfigurationService.java
index 5b5a435..9cd0b3c 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/ConfigurationService.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/ConfigurationService.java
@@ -8,4 +8,5 @@ public interface ConfigurationService {
 
     List<String> getDefaultConfigurationNames();
 
+
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/ConfigurationServiceImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/ConfigurationServiceImpl.java
index 3d95205..861f5da 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/ConfigurationServiceImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/ConfigurationServiceImpl.java
@@ -1,32 +1,37 @@
 package cz.zcu.fav.kiv.antipatterndetectionapp.service;
 
-import org.springframework.core.io.FileSystemResource;
+import cz.zcu.fav.kiv.antipatterndetectionapp.repository.ConfigurationRepository;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.io.File;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 
 @Service
 public class ConfigurationServiceImpl implements ConfigurationService {
 
-    private static final String configurationsLocation = "\\src\\main\\webapp\\configurations";
+    @Autowired
+    private ConfigurationRepository configurationRepository;
 
     @Override
     public List<String> getAllConfigurationNames() {
         List<String> configList = new ArrayList<String>();
-        File folder = new File(String.valueOf(Paths.get(new FileSystemResource("").getFile().getAbsolutePath() + configurationsLocation)));
-        for (final File fileEntry : folder.listFiles()) {
-            configList.add(fileEntry.getName().split("\\.")[0]);
+
+        // get all configurations
+        for (String key : configurationRepository.allConfigurations.keySet() ) {
+            configList.add(key);
         }
+
         return configList;
     }
 
     @Override
     public List<String> getDefaultConfigurationNames() {
         List<String> configList = new ArrayList<String>();
+
+        // insert only default configurations
         configList.add("Default");
+
         return configList;
     }
 
diff --git a/src/main/webapp/WEB-INF/templates/index.html b/src/main/webapp/WEB-INF/templates/index.html
index 0be8544..b724314 100644
--- a/src/main/webapp/WEB-INF/templates/index.html
+++ b/src/main/webapp/WEB-INF/templates/index.html
@@ -44,7 +44,7 @@
                     <tr th:each="project : ${query.projects}">
                         <td th:text="${project.id}"></td>
                         <td th:text="${project.name}"></td>
-                        <td><a th:href="@{/projects/} + ${project.id}">Show</a></td>
+                        <td><a th:href="@{'/projects/' + ${project.id}}">Show</a></td>
                         <td style="text-align: center">
                             <input checked type="checkbox" class="form-check-input" name="selectedProjects"
                                    th:value="${project.id}" th:id="@{project_} + ${project.id}">
@@ -79,7 +79,7 @@
                     <tr th:each="antiPattern : ${query.antiPatterns}">
                         <td th:text="${antiPattern.id}"></td>
                         <td th:text="${antiPattern.printName}"></td>
-                        <td><a th:href="@{/anti-patterns/} + ${antiPattern.id}">Show</a></td>
+                        <td><a th:href="@{'/anti-patterns/' + ${antiPattern.id}}">Show</a></td>
                         <td style="text-align: center">
                             <input checked type="checkbox" class="form-check-input" name="selectedAntiPatterns"
                                    th:value="${antiPattern.id}" th:id="@{anti-pattern_} + ${antiPattern.id}">
diff --git a/src/main/webapp/configurations/Default.json b/src/main/webapp/configurations/Default.json
index 51b45a1..9531ca4 100644
--- a/src/main/webapp/configurations/Default.json
+++ b/src/main/webapp/configurations/Default.json
@@ -1,90 +1,111 @@
 {
-    "name" : "Default",
-    "default" : "1",
-    "TooLongSprint": [
-        {
-            "thresholdName": "maxIterationLength",
-            "value": "21"
-        },
-        {
-            "thresholdName": "maxNumberOfTooLongIterations",
-            "value": "0"
-        }
-    ],
-    "VaryingSprintLength": [
-        {
-            "thresholdName": "maxDaysDifference",
-            "value": "7"
+    "configuration": [
+        {
+            "antiPattern": "TooLongSprint",
+            "thresholds": [
+                {
+                    "thresholdName": "maxIterationLength",
+                    "value": "21"
+                },
+                {
+                    "thresholdName": "maxNumberOfTooLongIterations",
+                    "value": "0"
+                }
+            ]
         },
         {
-            "thresholdName": "maxIterationChanged",
-            "value": "1"
-        }
-    ],
-    "BusinessAsUsual": [
-        {
-            "thresholdName": "divisionOfIterationsWithRetrospective",
-            "value": "66.66f"
+            "antiPattern": "VaryingSprintLength",
+            "thresholds": [
+                {
+                    "thresholdName": "maxDaysDifference",
+                    "value": "7"
+                },
+                {
+                    "thresholdName": "maxIterationChanged",
+                    "value": "1"
+                }
+            ]
         },
         {
-           "thresholdName": "searchSubstringsWithRetrospective",
-            "value": "%retr%||%revi%||%week%scrum%"
-        }
-    ],
-    "SpecifyNothing": [
-        {
-            "thresholdName": "minNumberOfWikiPagesWithSpecification",
-            "value": "1"
+            "antiPattern": "BusinessAsUsual",
+            "thresholds": [
+                {
+                    "thresholdName": "divisionOfIterationsWithRetrospective",
+                    "value": "66.66f"
+                },
+                {
+                    "thresholdName": "searchSubstringsWithRetrospective",
+                    "value": "%retr%||%revi%||%week%scrum%"
+                }
+            ]
         },
         {
-            "thresholdName": "minNumberOfActivitiesWithSpecification",
-            "value": "1"
+            "antiPattern": "SpecifyNothing",
+            "thresholds": [
+                {
+                    "thresholdName": "minNumberOfWikiPagesWithSpecification",
+                    "value": "1"
+                },
+                {
+                    "thresholdName": "minNumberOfActivitiesWithSpecification",
+                    "value": "1"
+                },
+                {
+                    "thresholdName": "minAvgLengthOfActivityDescription",
+                    "value": "150"
+                },
+                {
+                    "thresholdName": "searchSubstringsWithProjectSpecification",
+                    "value": "%dsp%||%specifikace%||%specification%||%vize%proj%||%vize%produ%"
+                }
+            ]
         },
         {
-            "thresholdName": "minAvgLengthOfActivityDescription",
-            "value": "150"
+            "antiPattern": "RoadToNowhere",
+            "thresholds": [
+                {
+                    "thresholdName": "minNumberOfWikiPagesWithProjectPlan",
+                    "value": "1"
+                },
+                {
+                    "thresholdName": "minNumberOfActivitiesWithProjectPlan",
+                    "value": "1"
+                },
+                {
+                    "thresholdName": "searchSubstringsWithProjectPlan",
+                    "value": "%plán projektu%||%project plan%||%plan project%||%projektový plán%"
+                }
+            ]
         },
         {
-            "thresholdName": "searchSubstringsWithProjectSpecification",
-            "value": "%dsp%||%specifikace%||%specification%||%vize%proj%||%vize%produ%"
-        }
-    ],
-    "RoadToNowhere": [
-        {
-            "thresholdName": "minNumberOfWikiPagesWithProjectPlan",
-            "value": "1"
-        },
-        {
-            "thresholdName": "minNumberOfActivitiesWithProjectPlan",
-            "value": "1"
-        },
-        {
-            "thresholdName": "searchSubstringsWithProjectPlan",
-            "value": "%plán projektu%||%project plan%||%plan project%||%projektový plán%"
-        }
-    ],
-    "LongOrNonExistentFeedbackLoops": [
-        {
-            "thresholdName": "divisionOfIterationsWithFeedbackLoop",
-            "value": "50.00f"
-        },
-        {
-            "thresholdName": "maxGapBetweenFeedbackLoopRate",
-            "value": "2f"
-        },
-        {
-            "thresholdName": "searchSubstringsWithFeedbackLoop",
-            "value": "%schůz%zákazník%||%předvedení%zákazník%||%zákazn%demo%||%schůz%zadavat%||%inform%schůz%||%zákazn%||%zadavatel%"
-        }
-    ],
-    "NinetyNinetyRule": [
-        {
-            "thresholdName": "maxDivisionRange",
-            "value": "1.25f"
+            "antiPattern": "LongOrNonExistentFeedbackLoops",
+            "thresholds": [
+                {
+                    "thresholdName": "divisionOfIterationsWithFeedbackLoop",
+                    "value": "50.00f"
+                },
+                {
+                    "thresholdName": "maxGapBetweenFeedbackLoopRate",
+                    "value": "2f"
+                },
+                {
+                    "thresholdName": "searchSubstringsWithFeedbackLoop",
+                    "value": "%schůz%zákazník%||%předvedení%zákazník%||%zákazn%demo%||%schůz%zadavat%||%inform%schůz%||%zákazn%||%zadavatel%"
+                }
+            ]
         },
         {
-            "thresholdName": "maxBadDivisionLimit",
-            "value": "2"
+            "antiPattern": "NinetyNinetyRule",
+            "thresholds": [
+                {
+                    "thresholdName": "maxDivisionRange",
+                    "value": "1.25f"
+                },
+                {
+                    "thresholdName": "maxBadDivisionLimit",
+                    "value": "2"
+                }
+            ]
         }
     ]
 }
\ No newline at end of file
