bool DefineSliceOnValueDiscriminator(Int32 index, CodeBlockNested sliceDiscriminators, ElementDefinitionNode sliceNode, String varName, ElementDefinition.DiscriminatorComponent discriminator, String valueFilterMethod, String leafType) { const String fcn = nameof(DefineSliceOnValueDiscriminator); var selectedNodes = sliceNode.Select(discriminator.Path).ToArray(); var fixedNodes = selectedNodes.FixedValues().ToArray(); Element fixedValue = fixedNodes.SingleOrDefault(); if (fixedValue == null) { this.gen.ConversionError(this.GetType().Name, fcn, $"Slice node lacks fixed element {discriminator.Path}"); return(false); } String sliceName = sliceNode.Element.SliceName; CodeBlockNested valueMethodBlock = sliceClassMethods.AppendBlock(); valueFilterMethod = CSMisc.ValueFilterName(CSMisc.MakePath(sliceNode.SlicePath(), discriminator.Path)); // Note: We are defining method here, after we know the return value type. valueMethodBlock .BlankLine() .SummaryOpen() .AppendCode($"/// Return all elements for discriminator # {index+1}'") .SummaryLines(discriminator.ToFormatedJson()) .SummaryClose() ; { GenerateFhirPathSearch g = new GenerateFhirPathSearch(this.gen); if (g.Generate(valueMethodBlock, "static", valueFilterMethod, elementNode, discriminator.Path, out Type leaf) == false) { return(false); } leafType = leaf.FriendlyName(); } String tempVarName = $"sliceOnValueDiscriminator"; sliceDiscriminators .OpenBrace() .AppendLine($"/// Define discriminator'") .AppendLines("/// ", discriminator.ToFormatedJson().ToLines()) .AppendCode($"var {tempVarName} = new SliceOnValueDiscriminator<{baseItemTypeName}, {leafType}>()") .OpenBrace() .AppendCode($"Path = \"{discriminator.Path}\",") .AppendCode($"ValueFilter = {valueFilterMethod}") .CloseBrace(";") ; ElementFixCode.Construct(sliceDiscriminators, fixedValue, $"{tempVarName}.Pattern", out String propertyType); sliceDiscriminators .AppendCode($"{varName} = {tempVarName};") .CloseBrace("") ; return(true); }
void CreateSliceAccessorClass(ElementDefinitionNode sliceNode, out String sliceClassName, out String sliceInterfaceName) { const String fcn = nameof(CreateSliceAccessorClass); String sliceName = sliceNode.Element.SliceName.ToMachineName(); sliceClassName = $"{sliceName}Impl"; sliceInterfaceName = $"I{sliceName}"; String sliceBaseClassName; String sliceBaseInterfaceName; String baseType = elementNode.FhirType.FriendlyName(); // Debug.Assert(sliceClassName != "BreastradAbnormalityDensityImpl"); switch (SliceAccessorType(sliceNode)) { case SliceAccessorTypes.Error: sliceBaseClassName = "??"; sliceBaseInterfaceName = "??"; break; case SliceAccessorTypes.Single: sliceBaseClassName = $"SliceListAccessorSingle<{accessorType}>"; sliceBaseInterfaceName = $"ISliceAccessorSingle<{accessorType}>"; break; case SliceAccessorTypes.Multiple: sliceBaseClassName = $"SliceListAccessorMultiple<{accessorType}>"; sliceBaseInterfaceName = $"ISliceAccessorMultiple<{accessorType}>"; break; default: throw new NotImplementedException("Unknown SliceAccessorTypes value"); } String elementJson = sliceNode.Element.ToFormatedJson(); this.subClassBlock .BlankLine() .SummaryOpen() .Summary($"public interface that implements the functionality of slice {sliceClassName}") .SummaryClose() .AppendCode($"public interface {sliceInterfaceName} : {sliceBaseInterfaceName}") .OpenBrace() .CloseBrace() .BlankLine() .SummaryOpen() .Summary($"private class that implements the functionality of slice {sliceClassName}") .Summary("") .Summary(elementJson.ToLines()) .SummaryClose() .AppendCode($"class {sliceClassName} : {sliceBaseClassName}, {sliceInterfaceName}") .OpenBrace() .DefineBlock(out this.sliceStaticFields) .DefineBlock(out CodeBlockNested staticConstructorHeader) .DefineBlock(out this.sliceClassFields) .DefineBlock(out this.sliceClassMethods) .CloseBrace() ; staticConstructorHeader .SummaryOpen() .Summary("Static constructor") .SummaryClose() .AppendLine("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Performance\", \"CA1810:Initialize reference type static fields inline\")]") .AppendCode($"static {sliceClassName}()") .OpenBrace() .DefineBlock(out this.sliceStaticConstructor) .CloseBrace() ; if (sliceName == null) { this.gen.ConversionError(this.GetType().Name, fcn, $"Slice node '{elementNode.FullPath()}' lacks slice name"); retVal = false; } else { const String sliceFieldName = "slicing"; CreateConstructor(sliceClassName, sliceFieldName); this.sliceStaticFields .BlankLine() .SummaryOpen() .Summary($"slicing discriminator for {elementNode.FullPath()} slice {sliceName}") .SummaryClose() .AppendCode($"static Slicing<{baseItemTypeName}> {sliceFieldName};") ; this.sliceStaticConstructor .BlankLine() ; this.sliceStaticConstructor .AppendLine("// Instantiate slicing discriminator") .OpenBrace() .AppendCode($"ISliceDiscriminator<{baseItemTypeName}>[] discriminators = ") .AppendCode($" new ISliceDiscriminator<{baseItemTypeName}>[{discriminators.Length}];") ; for (Int32 i = 0; i < discriminators.Length; i++) { if (DefineDiscriminator(i, this.sliceStaticConstructor, sliceNode, $"discriminators[{i}]", discriminators[i]) == false) { retVal = false; } } this.sliceStaticConstructor .AppendCode($"{sliceFieldName} = new Slicing<{baseItemTypeName}>") .OpenBrace() .AppendCode($"Discriminators = discriminators") .CloseBrace(";") .CloseBrace() ; } // Recursively create code to set values that are fixed in object. // If a child object needs to be fixed, make sure that parent objects are created // as well. // i.e. if a.b.c = fix(...) // than we need to create a and a.b as well as setting a.b.c. void SetFixedValues(ElementDefinitionNode setNode, String propertyPath) { Int32 varNum = 0; foreach (ElementDefinitionNode setNodeChild in setNode.ChildNodes) { String childPropertyPath = $"{propertyPath}.{setNodeChild.PropertyName}"; String childItemTypeName = setNodeChild.FhirItemType.FriendlyName(); if (setNodeChild.IsFixed) { if (setNodeChild.IsListType) { String varName = $"var{varNum}"; varNum += 1; ElementFixCode.Construct(sliceClassMethods, setNodeChild.Element.Fixed, $"{varName}", out String propertyType); sliceClassMethods.AppendCode($"{childPropertyPath}.Add({varName});"); } else { ElementFixCode.Construct(sliceClassMethods, setNodeChild.Element.Fixed, childPropertyPath, out String propertyType); } } else if (setNodeChild.HasFixedChild) { String varName = $"var{varNum}"; varNum += 1; sliceClassMethods.AppendCode($"{childItemTypeName} {varName} = new {childItemTypeName}();"); if (setNodeChild.IsListType) { sliceClassMethods.AppendCode($"{childPropertyPath}.Add({varName});"); } else { sliceClassMethods.AppendCode($"{childPropertyPath} = {varName};"); } SetFixedValues(setNodeChild, varName); } else if (setNodeChild.HasFixedSlice) { String varName = $"var{varNum}"; varNum += 1; sliceClassMethods.AppendCode($"// {childItemTypeName} {varName} = xxyyz;"); //methods.AppendCode($"{childItemTypeName} {varName} = new {childItemTypeName}();"); //if (setNodeChild.IsListType) // methods.AppendCode($"{childPropertyPath}.Add({varName});"); //else // methods.AppendCode($"{childPropertyPath} = {varName};"); //SetFixedValues(setNodeChild, varName); } } } sliceClassMethods .BlankLine() .SummaryOpen() .Summary($"Create and initialize a new item") .SummaryClose() .AppendCode($"protected override {accessorType} Create()") .OpenBrace() .AppendCode($"{accessorType} retVal = new {accessorType}();") ; SetFixedValues(sliceNode, "retVal"); sliceClassMethods .AppendCode($"return retVal;") .CloseBrace() ; }