package stirling.software.SPDF.config; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; public class ConfigInitializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext applicationContext) { try { ensureConfigExists(); } catch (IOException e) { throw new RuntimeException("Failed to initialize application configuration", e); } } public void ensureConfigExists() throws IOException { // Define the path to the external config directory Path destPath = Paths.get("configs", "settings.yml"); // Check if the file already exists if (Files.notExists(destPath)) { // Ensure the destination directory exists Files.createDirectories(destPath.getParent()); // Copy the resource from classpath to the external directory try (InputStream in = getClass().getClassLoader().getResourceAsStream("settings.yml.template")) { if (in != null) { Files.copy(in, destPath); } else { throw new FileNotFoundException( "Resource file not found: settings.yml.template"); } } } else { // If user file exists, we need to merge it with the template from the classpath List templateLines; try (InputStream in = getClass().getClassLoader().getResourceAsStream("settings.yml.template")) { templateLines = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)) .lines() .collect(Collectors.toList()); } mergeYamlFiles(templateLines, destPath, destPath); } } public void mergeYamlFiles(List templateLines, Path userFilePath, Path outputPath) throws IOException { List userLines = Files.readAllLines(userFilePath); List mergedLines = new ArrayList<>(); boolean insideAutoGenerated = false; boolean beforeFirstKey = true; Function isCommented = line -> line.trim().startsWith("#"); Function extractKey = line -> { String[] parts = line.split(":"); return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : ""; }; Set userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet()); for (String line : templateLines) { String key = extractKey.apply(line); if (line.trim().equalsIgnoreCase("AutomaticallyGenerated:")) { insideAutoGenerated = true; mergedLines.add(line); continue; } else if (insideAutoGenerated && line.trim().isEmpty()) { insideAutoGenerated = false; mergedLines.add(line); continue; } if (beforeFirstKey && (isCommented.apply(line) || line.trim().isEmpty())) { // Handle top comments and empty lines before the first key. mergedLines.add(line); continue; } if (!key.isEmpty()) beforeFirstKey = false; if (userKeys.contains(key)) { // If user has any version (commented or uncommented) of this key, skip the // template line Optional userValue = userLines.stream() .filter( l -> extractKey.apply(l).equalsIgnoreCase(key) && !isCommented.apply(l)) .findFirst(); if (userValue.isPresent()) mergedLines.add(userValue.get()); continue; } if (isCommented.apply(line) || line.trim().isEmpty() || !userKeys.contains(key)) { mergedLines.add( line); // If line is commented, empty or key not present in user's file, // retain the // template line continue; } } // Add any additional uncommented user lines that are not present in the // template for (String userLine : userLines) { String userKey = extractKey.apply(userLine); boolean isPresentInTemplate = templateLines.stream() .map(extractKey) .anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey)); if (!isPresentInTemplate && !isCommented.apply(userLine)) { mergedLines.add(userLine); } } Files.write(outputPath, mergedLines, StandardCharsets.UTF_8); } }