13 string? schemeVersion =
null)
15 if (
string.IsNullOrWhiteSpace(text))
17 throw new FormatException(
18 "Universal notation cannot be empty.");
27 throw new NotSupportedException(
28 $
"Universal notation scheme version '{version}' " +
35 StringSplitOptions.TrimEntries);
37 if (sections.Length != 3)
39 throw new FormatException(
40 "Universal notation must contain exactly three " +
41 "alpha | beta | gamma sections.");
45 ParseAlpha(sections[0]),
46 ParseBeta(sections[1]),
47 ParseGamma(sections[2]),
53 string? schemeVersion =
null)
64 StringSplitOptions.RemoveEmptyEntries |
65 StringSplitOptions.TrimEntries);
78 foreach (
string token
in tokens)
88 StringComparison.OrdinalIgnoreCase))
93 else if (token ==
"?")
101 StringComparison.OrdinalIgnoreCase))
106 productStructureType =
112 StringComparison.OrdinalIgnoreCase))
117 productStructureType =
118 UniversalNotationTokenCatalog
119 .ParseProductStructureCode(
125 StringComparison.OrdinalIgnoreCase))
133 StringComparison.OrdinalIgnoreCase))
135 if (network is not
null)
137 throw new FormatException(
138 "Alpha section contains more than one network token.");
141 network = ParseNetwork(token);
145 throw new FormatException(
146 $
"Unknown alpha token '{token}'.");
152 throw new FormatException(
153 "Alpha section must contain a network token.");
156 return new UniversalNotationAlpha
158 ItemCardinality = cardinality,
159 ProblemLevel = level,
165 private static UniversalNetworkNotation ParseNetwork(
168 string working = token.Trim();
173 string modifiersText =
string.Empty;
178 StringComparison.OrdinalIgnoreCase))
180 int closeIndex = working.IndexOf(
')');
184 throw new FormatException(
185 "Closed-loop network token is missing ')'.");
191 closeIndex -
"Net:CL(".Length);
196 StringSplitOptions.TrimEntries);
198 if (parts.Length != 2 ||
199 !parts[0].StartsWith(
201 StringComparison.OrdinalIgnoreCase) ||
202 !parts[1].StartsWith(
204 StringComparison.OrdinalIgnoreCase))
206 throw new FormatException(
207 "Closed-loop network must use " +
208 "Net:CL(F:<topology>;R:<topology>).");
214 UniversalNotationTokenCatalog
215 .ParseNetworkTopologyCode(
219 UniversalNotationTokenCatalog
220 .ParseNetworkTopologyCode(
223 if (closeIndex + 1 < working.Length)
225 if (working[closeIndex + 1] !=
':')
227 throw new FormatException(
228 "Invalid network modifier separator.");
232 working[(closeIndex + 2)..];
240 StringSplitOptions.RemoveEmptyEntries |
241 StringSplitOptions.TrimEntries);
243 if (parts.Length < 2 ||
246 StringComparison.OrdinalIgnoreCase))
248 throw new FormatException(
249 $
"Invalid network token '{token}'.");
258 StringComparison.OrdinalIgnoreCase))
263 UniversalNotationTokenCatalog
264 .ParseNetworkTopologyCode(
277 UniversalNotationTokenCatalog
278 .ParseNetworkTopologyCode(
287 parts.Skip(modifierStart));
290 int? echelonCount =
null;
291 bool hasCycles =
false;
292 bool hasMultiSourcing =
false;
293 bool hasTransshipment =
false;
295 if (!
string.IsNullOrWhiteSpace(modifiersText))
299 in modifiersText.Split(
301 StringSplitOptions.RemoveEmptyEntries |
302 StringSplitOptions.TrimEntries))
307 StringComparison.OrdinalIgnoreCase))
311 out
int parsedEchelon) ||
314 throw new FormatException(
315 $
"Invalid echelon modifier '{modifier}'.");
318 echelonCount = parsedEchelon;
323 StringComparison.OrdinalIgnoreCase))
330 StringComparison.OrdinalIgnoreCase))
332 hasMultiSourcing =
true;
337 StringComparison.OrdinalIgnoreCase))
339 hasTransshipment =
true;
343 throw new FormatException(
344 $
"Unknown network modifier '{modifier}'.");
349 return new UniversalNetworkNotation
352 ForwardTopology = forwardTopology,
353 ReverseTopology = reverseTopology,
354 EchelonCount = echelonCount,
355 HasCycles = hasCycles,
356 HasMultiSourcing = hasMultiSourcing,
357 HasTransshipment = hasTransshipment
361 private static UniversalNotationBeta ParseBeta(
367 StringComparison.OrdinalIgnoreCase))
369 return new UniversalNotationBeta();
375 StringSplitOptions.RemoveEmptyEntries |
376 StringSplitOptions.TrimEntries);
379 new List<UniversalNotationFeature>();
381 var temporalQualifiers =
382 new List<UniversalTemporalQualifier>();
384 var semanticConditions =
385 new List<UniversalSemanticCondition>();
387 foreach (
string token
in tokens)
390 UniversalNotationTokenCatalog
391 .TryParseTemporalQualifier(
395 temporalQualifiers.Add(
402 UniversalNotationTokenCatalog
403 .TryParseSemanticCondition(
407 semanticConditions.Add(
413 if (!UniversalNotationTokenCatalog.TryParseFeature(
417 throw new FormatException(
418 $
"Unknown beta token '{token}'.");
421 features.Add(feature);
424 return new UniversalNotationBeta(
430 private static UniversalNotationGamma ParseGamma(
434 section.Trim().ToUpperInvariant()
switch
448 _ =>
throw new FormatException(
449 $
"Unknown gamma token '{section.Trim()}'.")
452 return new UniversalNotationGamma
454 Objective = objective