From 547eeaf5ba64e400a87d178149f4541c513e6900 Mon Sep 17 00:00:00 2001 From: PJVervoorn Date: Sun, 22 Jun 2025 10:40:25 +0200 Subject: [PATCH] Added support for settings referencing settings. --- CHANGELOG.md | 6 + MODLICENSE.md | 12 ++ README.md | 9 ++ src/Exporter/Constants.cs | 13 ++ .../EnvironmentSettingsExporter.csproj | 2 + .../InternalExtensions/DataTableExtensions.cs | 125 ++++++++++++++++++ src/Exporter/Program.cs | 16 ++- src/Exporter/Properties/AssemblyInfo.cs | 1 + 8 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 MODLICENSE.md create mode 100644 src/Exporter/Constants.cs create mode 100644 src/Exporter/InternalExtensions/DataTableExtensions.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index fb6712a..a3ac0b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Version 1.7R (6/22/2025) + +Changes in this release: + +* NEW: Add support for references in settings + ## Version 1.7 (7/8/2020) Changes in this release: diff --git a/MODLICENSE.md b/MODLICENSE.md new file mode 100644 index 0000000..0b3ce38 --- /dev/null +++ b/MODLICENSE.md @@ -0,0 +1,12 @@ +# Modifications in This Fork + +This fork of [tfabraham/EnvironmentSettingsManager](https://github.com/tfabraham/EnvironmentSettingsManager) includes changes made by @PJVervoorn. + +All changes authored by @PJVervoorn are released under the [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/) license. + +These contributions are dedicated to the public domain and may be used freely, for any purpose, without attribution or restriction. + +The original repository is licensed under the MIT License, which remains in effect for all unmodified portions of the code. + +Original project: https://github.com/tfabraham/EnvironmentSettingsManager +Original license: [MIT License](LICENSE) diff --git a/README.md b/README.md index f0f5c30..da286c1 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,17 @@ The Environment Settings Manager consists of an Excel spreadsheet and an associa The Environment Settings Manager is also integrated into the [Deployment Framework for BizTalk](https://github.com/BTDF). +## Modifications +The modifications to the Environment Settings Manager enable settings to reference other settings. +These references are in the well known format ${SettingName} and a setting can contain 0, one or more references, e.g. ${DefaultDataDrive}Archive\${ProductCode}\ +Settings do not need to be in a specific order, the modifications will resolve the references in a loop (if necessary). + +### See [MODLICENSE.md](MODLICENSE.md) for license notes for this fork. + ## License Copyright (c) Thomas F. Abraham. All rights reserved. Licensed under the [MIT](LICENSE.txt) License. + +Modifications authored by @PJVervoorn are released under the [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/) license. \ No newline at end of file diff --git a/src/Exporter/Constants.cs b/src/Exporter/Constants.cs new file mode 100644 index 0000000..4e76137 --- /dev/null +++ b/src/Exporter/Constants.cs @@ -0,0 +1,13 @@ +namespace EnvironmentSettingsExporter +{ + internal class Constants + { + internal const int SETTINGSNAMECOLUMN = 0; + internal const int DEFAULTVALUECOLUMN = 1; + + internal const int ENVIRONMENTNAMEROW = 1; + internal const int GENERATEFILEROW = 2; + internal const int FILENAMEROW = 3; + internal const int FIRSTVALUEROW = 6; + } +} \ No newline at end of file diff --git a/src/Exporter/EnvironmentSettingsExporter.csproj b/src/Exporter/EnvironmentSettingsExporter.csproj index 0ab0397..ba03158 100644 --- a/src/Exporter/EnvironmentSettingsExporter.csproj +++ b/src/Exporter/EnvironmentSettingsExporter.csproj @@ -43,11 +43,13 @@ + + diff --git a/src/Exporter/InternalExtensions/DataTableExtensions.cs b/src/Exporter/InternalExtensions/DataTableExtensions.cs new file mode 100644 index 0000000..d5f2a5b --- /dev/null +++ b/src/Exporter/InternalExtensions/DataTableExtensions.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text.RegularExpressions; + +namespace EnvironmentSettingsExporter.InternalExtensions +{ + internal static class DataTableExtensions + { + private const string refPattern = @"\$\{([^}]+)\}"; + + internal static bool TryGetCellValue(this DataTable settingsTable, int rowIndex, int columnIndex, out string val) + { return !string.IsNullOrEmpty((val = settingsTable.GetCellValue(rowIndex, columnIndex))); } + + internal static void ResolveReferences(this DataTable settingsTable) + { + var prevColour = Console.ForegroundColor; + try + { + Console.ForegroundColor = ConsoleColor.DarkGray; + var refRegex = new Regex(refPattern); + // Start with the first column past default values + for (int columnIndex = Constants.DEFAULTVALUECOLUMN + 1; + columnIndex < settingsTable.Columns.Count && !settingsTable.Rows[Constants.FILENAMEROW].IsNull(columnIndex); + columnIndex++) + { + if (!settingsTable.TryGetCellValue(Constants.GENERATEFILEROW, columnIndex, out var val) + || "yes".Equals(val, System.StringComparison.InvariantCultureIgnoreCase) + || "true".Equals(val, System.StringComparison.InvariantCultureIgnoreCase)) + { + Console.WriteLine($"Resolving references in '{settingsTable.GetCellValue(Constants.ENVIRONMENTNAMEROW, columnIndex)}'"); + + var resolvedItems = new List>(); + var unresolvedItems = new List>(); + var rowCount = settingsTable.Rows.Count; + for (var rowIndex = Constants.FIRSTVALUEROW; rowIndex < rowCount; rowIndex++) + { + var sn = settingsTable.GetCellValue(rowIndex, Constants.SETTINGSNAMECOLUMN); + if (!string.IsNullOrEmpty(sn)) + { + var sv = settingsTable.GetSettingValue(rowIndex, columnIndex); + var t = new Tuple(sn, rowIndex, sv); + if (refRegex.IsMatch(sv)) + { unresolvedItems.Add(t); } + else + { resolvedItems.Add(t); } + } + } + + //Resolving the references. Continue as long as there are unresolved items which we can resolve + bool didResolveSomething = true; + while (unresolvedItems.Count > 0 && didResolveSomething) + { + didResolveSomething = false; + foreach (var item in unresolvedItems) + { + var result = refRegex.Replace(item.Item3, m => + { + if (resolvedItems.Exists(ri => ri.Item1.Equals(m.Groups[1].Value, StringComparison.InvariantCultureIgnoreCase))) + { + didResolveSomething = true; + return resolvedItems.Find(ri => ri.Item1.Equals(m.Groups[1].Value, StringComparison.InvariantCultureIgnoreCase)).Item3; + } + return m.Value; + }); + if (didResolveSomething) + { + var newItem = new Tuple(item.Item1, item.Item2, result); + unresolvedItems.Remove(item); + if (refRegex.IsMatch(result)) //we resolved something, but the cell requires more work. + { + Console.WriteLine($"\tPartially resolved reference(s) in '{item.Item1}'"); + unresolvedItems.Add(newItem); + } + else //completely resolved! + { + Console.WriteLine($"\tResolved reference(s) in '{item.Item1}'"); + resolvedItems.Add(newItem); + } + break; //Modified the collection, restart the loop + } + } + } + + if (unresolvedItems.Count > 0) + { throw new ApplicationException($"Settings file contains unresolved or circular references in: '{string.Join("', '", unresolvedItems.Select(t => t.Item1))}'"); } + + //update the table + foreach (var item in resolvedItems) + { settingsTable.Rows[item.Item2][columnIndex] = item.Item3; } + } + } + } + finally + { Console.ForegroundColor = prevColour; } + } + + #region Copied from DataTableToXmlExporter + internal static string GetCellValue(this DataTable settingsTable, int rowIndex, int columnIndex) + { + if (settingsTable.Rows[rowIndex].IsNull(columnIndex)) + { + return string.Empty; + } + else + { + return ((string)settingsTable.Rows[rowIndex][columnIndex]).Trim(); + } + } + + internal static string GetSettingValue(this DataTable settingsTable, int rowIndex, int columnIndex) + { + string value = GetCellValue(settingsTable, rowIndex, columnIndex); + + if (string.IsNullOrEmpty(value)) + { + value = GetCellValue(settingsTable, rowIndex, Constants.DEFAULTVALUECOLUMN); + } + + return value; + } + #endregion + } +} \ No newline at end of file diff --git a/src/Exporter/Program.cs b/src/Exporter/Program.cs index bb846c8..f2acf13 100644 --- a/src/Exporter/Program.cs +++ b/src/Exporter/Program.cs @@ -4,9 +4,12 @@ using System; using System.Data; using System.IO; +using System.Reflection; namespace EnvironmentSettingsExporter { + using InternalExtensions; + /// /// This application reads the deployment environment settings from an EnvironmentSettings.xls/.xml Excel file /// and exports them to one or more XML files. @@ -37,13 +40,11 @@ static int Main(string[] args) { ConsoleColor defaultConsoleColor = Console.ForegroundColor; - Version assemblyVersion = System.Reflection.Assembly.GetEntryAssembly().GetName().Version; - + var assemblyVersion = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine( - "Environment Settings Spreadsheet to XML Exporter " - + assemblyVersion.Major + "." + assemblyVersion.Minor + "." + assemblyVersion.Build); - Console.WriteLine("[https://github.com/tfabraham/EnvironmentSettingsManager]"); + Console.WriteLine($"Environment Settings Spreadsheet to XML Exporter {assemblyVersion} (Reference Edition)"); + Console.WriteLine("[https://github.com/PJVervoorn/EnvironmentSettingsManager]"); + Console.WriteLine("Forked from [https://github.com/tfabraham/EnvironmentSettingsManager]"); Console.WriteLine("Copyright (C) 2007 Thomas F. Abraham. All Rights Reserved."); Console.WriteLine(); Console.ForegroundColor = defaultConsoleColor; @@ -102,6 +103,9 @@ static int Main(string[] args) settingsTable.Merge(SettingsFileReader.ReadSettingsFromExcelFile(inputFile2)); } + //Resolve references to settings with their values + settingsTable.ResolveReferences(); + if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); diff --git a/src/Exporter/Properties/AssemblyInfo.cs b/src/Exporter/Properties/AssemblyInfo.cs index 9baed55..311af8e 100644 --- a/src/Exporter/Properties/AssemblyInfo.cs +++ b/src/Exporter/Properties/AssemblyInfo.cs @@ -31,3 +31,4 @@ // [assembly: AssemblyVersion("1.7.0.0")] [assembly: AssemblyFileVersion("1.7.0.0")] +[assembly: AssemblyInformationalVersion("1.7.0.0 R")]