LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
MathematicalDomainKeyBuilder.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Globalization;
4using System.Linq;
5
7
8/// <summary>
9/// Builds stable business-domain keys for mathematical
10/// variables and constraints.
11/// </summary>
12/// <remarks>
13/// A domain key is composed of a category followed by ordered
14/// name-value segments:
15/// <code>
16/// inventory|item=1|warehouse=2|period=3
17/// </code>
18/// Segment names are compared using ordinal comparison and are
19/// emitted in insertion order.
20/// </remarks>
22{
23 private readonly List<KeyValuePair<string, string>> _segments =
24 new();
25
26 /// <summary>
27 /// Initializes a domain-key builder.
28 /// </summary>
29 /// <param name="category">
30 /// Domain-key category.
31 /// </param>
32 /// <exception cref="ArgumentException">
33 /// Thrown when <paramref name="category"/> is empty or
34 /// contains a reserved character.
35 /// </exception>
37 string category)
38 {
39 Category =
40 NormalizeToken(
41 category,
42 nameof(category));
43 }
44
45 /// <summary>
46 /// Gets the domain-key category.
47 /// </summary>
48 public string Category
49 {
50 get;
51 }
52
53 /// <summary>
54 /// Gets the number of added segments.
55 /// </summary>
56 public int SegmentCount =>
57 _segments.Count;
58
59 /// <summary>
60 /// Adds a text segment.
61 /// </summary>
62 /// <param name="name">
63 /// Segment name.
64 /// </param>
65 /// <param name="value">
66 /// Segment value.
67 /// </param>
68 /// <returns>
69 /// Current builder.
70 /// </returns>
72 string name,
73 string value)
74 {
75 string normalizedName =
76 NormalizeToken(
77 name,
78 nameof(name));
79
80 string normalizedValue =
81 NormalizeToken(
82 value,
83 nameof(value));
84
85 EnsureSegmentNameIsUnique(
86 normalizedName);
87
88 _segments.Add(
89 new KeyValuePair<string, string>(
90 normalizedName,
91 normalizedValue));
92
93 return this;
94 }
95
96 /// <summary>
97 /// Adds an integer segment.
98 /// </summary>
99 /// <param name="name">
100 /// Segment name.
101 /// </param>
102 /// <param name="value">
103 /// Segment value.
104 /// </param>
105 /// <returns>
106 /// Current builder.
107 /// </returns>
109 string name,
110 int value)
111 {
112 return Add(
113 name,
114 value.ToString(
115 CultureInfo.InvariantCulture));
116 }
117
118 /// <summary>
119 /// Adds a long integer segment.
120 /// </summary>
121 /// <param name="name">
122 /// Segment name.
123 /// </param>
124 /// <param name="value">
125 /// Segment value.
126 /// </param>
127 /// <returns>
128 /// Current builder.
129 /// </returns>
131 string name,
132 long value)
133 {
134 return Add(
135 name,
136 value.ToString(
137 CultureInfo.InvariantCulture));
138 }
139
140 /// <summary>
141 /// Adds a globally unique identifier segment.
142 /// </summary>
143 /// <param name="name">
144 /// Segment name.
145 /// </param>
146 /// <param name="value">
147 /// Segment value.
148 /// </param>
149 /// <returns>
150 /// Current builder.
151 /// </returns>
153 string name,
154 Guid value)
155 {
156 return Add(
157 name,
158 value.ToString(
159 "D",
160 CultureInfo.InvariantCulture));
161 }
162
163 /// <summary>
164 /// Adds an enumeration segment.
165 /// </summary>
166 /// <typeparam name="TEnum">
167 /// Enumeration type.
168 /// </typeparam>
169 /// <param name="name">
170 /// Segment name.
171 /// </param>
172 /// <param name="value">
173 /// Segment value.
174 /// </param>
175 /// <returns>
176 /// Current builder.
177 /// </returns>
179 string name,
180 TEnum value)
181 where TEnum : struct, Enum
182 {
183 return Add(
184 name,
185 value.ToString());
186 }
187
188 /// <summary>
189 /// Adds a segment only when the supplied text value is not
190 /// empty.
191 /// </summary>
192 /// <param name="name">
193 /// Segment name.
194 /// </param>
195 /// <param name="value">
196 /// Optional segment value.
197 /// </param>
198 /// <returns>
199 /// Current builder.
200 /// </returns>
202 string name,
203 string? value)
204 {
205 if (!string.IsNullOrWhiteSpace(
206 value))
207 {
208 Add(
209 name,
210 value);
211 }
212
213 return this;
214 }
215
216 /// <summary>
217 /// Builds the immutable domain-key text.
218 /// </summary>
219 /// <returns>
220 /// Stable domain key.
221 /// </returns>
222 public string Build()
223 {
224 if (_segments.Count == 0)
225 {
226 return Category;
227 }
228
229 return string.Join(
230 "|",
231 new[]
232 {
234 }.Concat(
235 _segments.Select(
236 segment =>
237 $"{segment.Key}={segment.Value}")));
238 }
239
240 /// <summary>
241 /// Returns the built domain key.
242 /// </summary>
243 /// <returns>
244 /// Stable domain key.
245 /// </returns>
246 public override string ToString()
247 {
248 return Build();
249 }
250
251 private void EnsureSegmentNameIsUnique(
252 string segmentName)
253 {
254 if (_segments.Any(
255 segment =>
256 string.Equals(
257 segment.Key,
258 segmentName,
259 StringComparison.Ordinal)))
260 {
261 throw new InvalidOperationException(
262 $"Domain-key segment '{segmentName}' is already " +
263 "defined.");
264 }
265 }
266
267 private static string NormalizeToken(
268 string value,
269 string parameterName)
270 {
271 if (string.IsNullOrWhiteSpace(
272 value))
273 {
274 throw new ArgumentException(
275 "A domain-key token cannot be empty.",
276 parameterName);
277 }
278
279 string normalizedValue =
280 value.Trim();
281
282 if (normalizedValue.Contains(
283 '|',
284 StringComparison.Ordinal) ||
285 normalizedValue.Contains(
286 '=',
287 StringComparison.Ordinal))
288 {
289 throw new ArgumentException(
290 "A domain-key token cannot contain the reserved " +
291 "characters '|' or '='.",
292 parameterName);
293 }
294
295 return normalizedValue;
296 }
297}
MathematicalDomainKeyBuilder Add(string name, int value)
Adds an integer segment.
MathematicalDomainKeyBuilder Add< TEnum >(string name, TEnum value)
Adds an enumeration segment.
MathematicalDomainKeyBuilder Add(string name, Guid value)
Adds a globally unique identifier segment.
MathematicalDomainKeyBuilder Add(string name, long value)
Adds a long integer segment.
MathematicalDomainKeyBuilder AddOptional(string name, string? value)
Adds a segment only when the supplied text value is not empty.
MathematicalDomainKeyBuilder Add(string name, string value)
Adds a text segment.
MathematicalDomainKeyBuilder(string category)
Initializes a domain-key builder.