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")]