16 ArgumentNullException.ThrowIfNull(reader);
17 var document = XDocument.Load(reader);
18 foreach (var chain
in document.Descendants().Where(e => e.Name.LocalName ==
"supplyChain"))
19 MigrateSupplyChain(chain);
23 private static void MigrateSupplyChain(XElement chain)
25 XNamespace ns = chain.Name.Namespace;
26 var version = (
string?)chain.Attribute(
"transportFormatVersion");
27 if (version is not
null && version !=
"1" && version !=
"2")
28 throw new InvalidOperationException($
"Unsupported transport format version '{version}'.");
29 var resources = chain.Element(ns +
"transportResources")?.Elements(ns +
"transportResource").ToArray() ?? [];
30 var containers = resources.SelectMany(r => r.Elements(ns +
"lanes")).ToArray();
31 bool central = chain.Element(ns +
"transportLanes") is not
null || chain.Element(ns +
"transportAssignments") is not
null;
32 if (chain.Element(ns +
"transportLanes")?.Elements(ns +
"transportLane").Any(l => l.Attribute(
"leadTime") is not
null) ==
true)
33 throw new InvalidOperationException(
"Central lanes cannot carry delays; put the delay on each assignment.");
34 if (containers.Length == 0)
36 if (version ==
"1" && central)
37 throw new InvalidOperationException(
"Version 1 cannot contain central transport data.");
38 if (version ==
"1") chain.SetAttributeValue(
"transportFormatVersion",
"2");
41 if (central || version ==
"2")
42 throw new InvalidOperationException(
"Mixed legacy and central transport representations cannot be merged implicitly.");
44 var entries = resources.SelectMany(resource => resource.Elements(ns +
"lanes")
45 .SelectMany(c => c.Elements()).Select(lane =>
47 if (lane.Name != ns +
"transportLane" || lane.Attributes().Any(a => a.Name !=
"leadTime") ||
48 lane.Elements().Any(e => e.Name != ns +
"origin" && e.Name != ns +
"destination"))
49 throw new InvalidOperationException(
"Unrecognized legacy lane data; migration would lose information.");
50 var origin = ReadWarehouse(lane, ns +
"origin");
51 var destination = ReadWarehouse(lane, ns +
"destination");
52 if (origin.Key == destination.Key) throw new InvalidOperationException(
"A legacy lane connects a warehouse to itself.");
53 int delay = ReadInteger(lane,
"leadTime");
54 int resourceId = ReadInteger(resource,
"id");
55 if (delay < 0 || resourceId < 0) throw new InvalidOperationException(
"Negative legacy delay or resource identifier.");
56 return new { ResourceId = resourceId, Delay = delay, Origin = origin.Element, Destination = destination.Element,
57 Key = (origin.Key.Kind, origin.Key.Id, destination.Key.Kind, destination.Key.Id) };
60 var lanes =
new XElement(ns +
"transportLanes");
61 var assignments =
new XElement(ns +
"transportAssignments");
64 foreach (var group
in entries.GroupBy(e => e.Key).OrderBy(g => g.Key))
67 var first = group.First();
68 lanes.Add(
new XElement(ns +
"transportLane",
new XAttribute(
"id",
id),
69 new XElement(first.Origin),
new XElement(first.Destination)));
70 foreach (var pair
in group.GroupBy(e => e.ResourceId).OrderBy(g => g.Key))
72 int[] delays = pair.Select(e => e.Delay).Distinct().ToArray();
73 if (delays.Length != 1)
74 throw new InvalidOperationException($
"Conflicting legacy delays for lane {group.Key}, resource {pair.Key}.");
75 assignments.Add(
new XElement(ns +
"transportAssignment",
new XAttribute(
"laneId",
id),
76 new XAttribute(
"transportResourceId", pair.Key),
new XAttribute(
"leadTime", delays[0])));
80 foreach (var container
in containers) container.Remove();
81 chain.Add(lanes, assignments);
82 chain.SetAttributeValue(
"transportFormatVersion",
"2");
85 private static ((
int Kind,
int Id) Key, XElement Element) ReadWarehouse(XElement lane, XName name)
87 var element = lane.Elements(name).SingleOrDefault()
88 ??
throw new InvalidOperationException($
"Missing legacy {name.LocalName}.");
89 string? kindText = (
string?)element.Attribute(
"kind");
90 if (!Enum.TryParse<
WarehouseReferenceKind>(kindText, ignoreCase:
true, out var kind) || !Enum.IsDefined(kind))
91 throw new InvalidOperationException($
"Invalid warehouse kind '{kindText}'.");
92 int id = ReadInteger(element,
"id");
93 if (
id < 0)
throw new InvalidOperationException(
"Negative warehouse identifier.");
94 return (((
int)kind,
id), element);
97 private static int ReadInteger(XElement element,
string name) =>
98 int.TryParse((
string?)element.Attribute(name), NumberStyles.Integer, CultureInfo.InvariantCulture, out
int value)
99 ? value :
throw new InvalidOperationException($
"Missing or invalid integer '{name}' in legacy transport data.");