LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
MathematicalDomainKey.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/// Represents a parsed mathematical-model business-domain key.
10/// </summary>
11/// <remarks>
12/// Domain keys use the canonical format:
13/// <code>
14/// category|name=value|name=value
15/// </code>
16/// For example:
17/// <code>
18/// inventory|item=1|warehouse=2|period=3
19/// </code>
20/// </remarks>
21public sealed class MathematicalDomainKey
22{
23 private readonly Dictionary<string, string> _segments =
24 new(
25 StringComparer.OrdinalIgnoreCase);
26
27 /// <summary>
28 /// Initializes a mathematical domain key.
29 /// </summary>
30 /// <param name="category">
31 /// Domain-key category.
32 /// </param>
34 string category)
35 {
36 if (string.IsNullOrWhiteSpace(
37 category))
38 {
39 throw new ArgumentException(
40 "A mathematical domain-key category is required.",
41 nameof(category));
42 }
43
44 Category =
45 category.Trim();
46 }
47
48 /// <summary>
49 /// Gets the domain-key category.
50 /// </summary>
51 public string Category
52 {
53 get;
54 }
55
56 /// <summary>
57 /// Gets the parsed name-value segments.
58 /// </summary>
59 public IReadOnlyDictionary<string, string> Segments =>
60 _segments;
61
62 /// <summary>
63 /// Gets the number of parsed segments.
64 /// </summary>
65 public int SegmentCount =>
66 _segments.Count;
67
68 /// <summary>
69 /// Determines whether a segment exists.
70 /// </summary>
71 /// <param name="name">
72 /// Segment name.
73 /// </param>
74 /// <returns>
75 /// <see langword="true"/> when the segment exists;
76 /// otherwise, <see langword="false"/>.
77 /// </returns>
78 public bool Contains(
79 string name)
80 {
81 return _segments.ContainsKey(
82 NormalizeName(
83 name));
84 }
85
86 /// <summary>
87 /// Gets a required text segment.
88 /// </summary>
89 /// <param name="name">
90 /// Segment name.
91 /// </param>
92 /// <returns>
93 /// Segment value.
94 /// </returns>
95 /// <exception cref="KeyNotFoundException">
96 /// Thrown when the segment does not exist.
97 /// </exception>
98 public string GetRequiredString(
99 string name)
100 {
101 string normalizedName =
102 NormalizeName(
103 name);
104
105 if (_segments.TryGetValue(
106 normalizedName,
107 out string? value))
108 {
109 return value;
110 }
111
112 throw new KeyNotFoundException(
113 $"Domain-key segment '{normalizedName}' is required.");
114 }
115
116 /// <summary>
117 /// Gets a required integer segment.
118 /// </summary>
119 /// <param name="name">
120 /// Segment name.
121 /// </param>
122 /// <returns>
123 /// Parsed integer value.
124 /// </returns>
126 string name)
127 {
128 string value =
130 name);
131
132 if (!int.TryParse(
133 value,
134 NumberStyles.Integer,
135 CultureInfo.InvariantCulture,
136 out int parsedValue))
137 {
138 throw new FormatException(
139 $"Domain-key segment '{name}' value '{value}' " +
140 "is not a valid 32-bit integer.");
141 }
142
143 return parsedValue;
144 }
145
146 /// <summary>
147 /// Attempts to get a text segment.
148 /// </summary>
149 /// <param name="name">
150 /// Segment name.
151 /// </param>
152 /// <param name="value">
153 /// Segment value when found.
154 /// </param>
155 /// <returns>
156 /// <see langword="true"/> when the segment exists;
157 /// otherwise, <see langword="false"/>.
158 /// </returns>
159 public bool TryGetString(
160 string name,
161 out string? value)
162 {
163 return _segments.TryGetValue(
164 NormalizeName(
165 name),
166 out value);
167 }
168
169 /// <summary>
170 /// Attempts to get an integer segment.
171 /// </summary>
172 /// <param name="name">
173 /// Segment name.
174 /// </param>
175 /// <param name="value">
176 /// Parsed integer value when successful.
177 /// </param>
178 /// <returns>
179 /// <see langword="true"/> when the segment exists and is a
180 /// valid integer; otherwise, <see langword="false"/>.
181 /// </returns>
182 public bool TryGetInt32(
183 string name,
184 out int value)
185 {
186 value =
187 default;
188
189 return TryGetString(
190 name,
191 out string? text) &&
192 int.TryParse(
193 text,
194 NumberStyles.Integer,
195 CultureInfo.InvariantCulture,
196 out value);
197 }
198
199 /// <summary>
200 /// Parses a canonical mathematical domain key.
201 /// </summary>
202 /// <param name="domainKey">
203 /// Domain-key text.
204 /// </param>
205 /// <returns>
206 /// Parsed mathematical domain key.
207 /// </returns>
208 /// <exception cref="ArgumentException">
209 /// Thrown when the domain key is empty.
210 /// </exception>
211 /// <exception cref="FormatException">
212 /// Thrown when the domain key contains malformed or duplicate
213 /// segments.
214 /// </exception>
216 string domainKey)
217 {
218 if (string.IsNullOrWhiteSpace(
219 domainKey))
220 {
221 throw new ArgumentException(
222 "A mathematical domain key is required.",
223 nameof(domainKey));
224 }
225
226 string[] parts =
227 domainKey
228 .Split(
229 '|',
230 StringSplitOptions.TrimEntries);
231
232 if (parts.Length == 0 ||
233 string.IsNullOrWhiteSpace(
234 parts[0]))
235 {
236 throw new FormatException(
237 "A mathematical domain key must start with a " +
238 "category.");
239 }
240
241 var result =
243 parts[0]);
244
245 foreach (
246 string part
247 in parts.Skip(
248 1))
249 {
250 int separatorIndex =
251 part.IndexOf(
252 '=',
253 StringComparison.Ordinal);
254
255 if (separatorIndex <= 0 ||
256 separatorIndex ==
257 part.Length - 1)
258 {
259 throw new FormatException(
260 $"Malformed mathematical domain-key segment " +
261 $"'{part}'. Expected 'name=value'.");
262 }
263
264 string name =
265 part[..separatorIndex].Trim();
266
267 string value =
268 part[(separatorIndex + 1)..].Trim();
269
270 if (name.Length == 0 ||
271 value.Length == 0)
272 {
273 throw new FormatException(
274 $"Malformed mathematical domain-key segment " +
275 $"'{part}'.");
276 }
277
278 if (!result._segments.TryAdd(
279 name,
280 value))
281 {
282 throw new FormatException(
283 $"Mathematical domain-key segment '{name}' " +
284 "appears more than once.");
285 }
286 }
287
288 return result;
289 }
290
291 /// <summary>
292 /// Attempts to parse a canonical mathematical domain key.
293 /// </summary>
294 /// <param name="domainKey">
295 /// Domain-key text.
296 /// </param>
297 /// <param name="result">
298 /// Parsed key when successful.
299 /// </param>
300 /// <returns>
301 /// <see langword="true"/> when parsing succeeds; otherwise,
302 /// <see langword="false"/>.
303 /// </returns>
304 public static bool TryParse(
305 string domainKey,
306 out MathematicalDomainKey? result)
307 {
308 try
309 {
310 result =
311 Parse(
312 domainKey);
313
314 return true;
315 }
316 catch (
317 ArgumentException)
318 {
319 result =
320 null;
321
322 return false;
323 }
324 catch (
325 FormatException)
326 {
327 result =
328 null;
329
330 return false;
331 }
332 }
333
334 /// <summary>
335 /// Returns the canonical textual representation.
336 /// </summary>
337 /// <returns>
338 /// Canonical domain key.
339 /// </returns>
340 public override string ToString()
341 {
342 if (_segments.Count == 0)
343 {
344 return Category;
345 }
346
347 return string.Join(
348 "|",
349 new[]
350 {
352 }.Concat(
353 _segments.Select(
354 segment =>
355 $"{segment.Key}={segment.Value}")));
356 }
357
358 private static string NormalizeName(
359 string name)
360 {
361 if (string.IsNullOrWhiteSpace(
362 name))
363 {
364 throw new ArgumentException(
365 "A domain-key segment name is required.",
366 nameof(name));
367 }
368
369 return name.Trim();
370 }
371}
bool Contains(string name)
Determines whether a segment exists.
MathematicalDomainKey(string category)
Initializes a mathematical domain key.
override string ToString()
Returns the canonical textual representation.
IReadOnlyDictionary< string, string > Segments
Gets the parsed name-value segments.
static bool TryParse(string domainKey, out MathematicalDomainKey? result)
Attempts to parse a canonical mathematical domain key.
string GetRequiredString(string name)
Gets a required text segment.
int GetRequiredInt32(string name)
Gets a required integer segment.
static MathematicalDomainKey Parse(string domainKey)
Parses a canonical mathematical domain key.
bool TryGetInt32(string name, out int value)
Attempts to get an integer segment.
bool TryGetString(string name, out string? value)
Attempts to get a text segment.