// Note: this used to be in ModuleJson.ToModuleSchema(), but it turns out it's really useful for // a field to have access to the schema it's part of... which is tricky when everything is immutable // and the schema also has to have references to the fields. So this code is ugly - and makes field testing // trickier - but at least it's pleasant to use elsewhere. internal ModuleSchema(ModuleJson json) { // Note: we populate everything other than the fields first, so that field // construction can rely on it. json.Validate(); Identifier = new ModuleIdentifier(json.Name !, json.ModelId !.Value, json.FamilyCode !.Value, json.FamilyNumberCode !.Value); InstrumentGroups = json.BuildInstrumentGroups(); PresetInstruments = InstrumentGroups.SelectMany(ig => ig.Instruments) .OrderBy(i => i.Id) .ToList() .AsReadOnly(); // Just validate that our list is consistent. for (int i = 0; i < PresetInstruments.Count; i++) { if (PresetInstruments[i].Id != i) { throw new InvalidOperationException($"Instrument {PresetInstruments[i]} is in index {i}"); } } UserSampleInstruments = Enumerable.Range(0, json.UserSamples !.Value) .Select(id => Instrument.FromUserSample(id)) .ToList() .AsReadOnly(); // Now do everything with the fields. Root = new FixedContainer(json.BuildRootContainer(this), new ModuleAddress(0)); LogicalRoot = json.BuildLogicalRoot(Root); PhysicalRoot = VisualTreeNode.FromFixedContainer(null, Root); KitRoots = LogicalRoot.DescendantNodesAndSelf() .Where(node => node.KitNumber != null) .ToDictionary(node => node.KitNumber !.Value) .AsReadOnly(); LoadableContainersByAddress = Root .DescendantsAndSelf() .Where(context => context.Container.Loadable) .ToDictionary(context => context.Address, context => context.Container) .AsReadOnly(); }