protected override void BuildItemWrite(CodeBlockNested b) { b .AppendCode("ResourceReference retVal = this.Value.ResourceReference();") ; }
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); }
protected override void BuildItemRead(CodeBlockNested b) { b .AppendCode($"this.Value = ({this.ElementGetName}) doc.GetResource(item);") ; }
void GenerateCIMPLDataType(CodeBlockNested entryBlock, CodeBlockNested mapBlock, FHIRAllTypes fhirType) { String fhirTypeName = ModelInfo.FhirTypeToFhirTypeName(fhirType); Type fhirCSType = ModelInfo.GetTypeForFhirType(fhirTypeName); entryBlock .BlankLine() .AppendLine($"// Fhir data element {fhirTypeName} definition") .AppendCode($"Entry: {fhirTypeName}") ; CodeBlockNested item = entryBlock.AppendBlock(); CodeBlockNested vars = entryBlock.AppendBlock(); foreach (PropertyInfo pi in fhirCSType.GetProperties()) { FhirElementAttribute attribute = pi.GetCustomAttribute <FhirElementAttribute>(); if (attribute != null) { String min; String max; Type csType; if (pi.PropertyType.IsList()) { min = "0"; max = "*"; csType = pi.PropertyType.GenericTypeArguments[0]; } if (pi.PropertyType.IsNullable()) { min = "0"; max = "1"; csType = pi.PropertyType.GenericTypeArguments[0]; } else { csType = pi.PropertyType; min = "1"; max = "1"; } String name = attribute.Name.ToMachineName(); item .AppendCode($"Property: {name} {min}..{max}") ; String typeName = null; String csTypeName = csType.FriendlyName(); switch (csTypeName) { case "string": OutputProperty(attribute, item, min, max); break; default: typeName = ModelInfo.GetFhirTypeNameForType(pi.PropertyType); if (String.IsNullOrEmpty(typeName)) { throw new Exception($"Can not determine fhir type for c# type {csTypeName}"); } break; } vars .BlankLine() .AppendCode($"Element: {name}") .AppendCode($"Value: {typeName}") ; // methods // .AppendCode($"case \"{attribute.Name}\":") // .OpenBrace() // .AppendCode($"ElementDefinition e = new ElementDefinition") // .OpenBrace() // .AppendCode($"Path = $\"{{parentPath}}.{attribute.Name}\",") // .AppendCode($"Short = \"{fhirType}.{attribute.Name} common attribute\",") // .AppendCode($"Min = {min},") // .AppendCode($"Max = \"{max}\"") // .CloseBrace(";") // .AppendCode($"retVal = new ElementNode(this, e, typeof({pi.PropertyType.FriendlyName()}), nameof({fhirCSType.FriendlyName()}.{pi.Name}));") // .AppendCode($"retVal.AutoGeneratedFlag = true;") // .AppendCode($"break;") // .CloseBrace(";") // ; } } }
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() ; }
void GenerateFindCommonChildren() { CodeEditor editor = new CodeEditor(); CodeBlockNested main = editor.Blocks.AppendBlock(); main .AppendLine($"using System;") .AppendLine($"using System.Linq;") .AppendLine($"using System.Collections.Generic;") .AppendLine($"using System.Reflection;") .AppendLine($"using System.Text;") .AppendLine($"using FhirKhit.Tools;") .AppendLine($"using Hl7.Fhir.Introspection;") .AppendLine($"using Hl7.Fhir.Model;") .AppendLine($"using Hl7.Fhir.Support.Model;") .AppendLine($"using System.Diagnostics;") .AppendLine($"using Hl7.FhirPath;") .BlankLine() #if FHIR_R3 .AppendLine($"namespace FhirKhit.Tools.R3") #elif FHIR_R4 .AppendLine($"namespace FhirKhit.Tools.R4") #endif .OpenBrace() .AppendCode($"public partial class ElementDefinitionNode") .OpenBrace() ; CodeBlockNested construct = main.AppendBlock(); CodeBlockNested methods = main.AppendBlock(); main .CloseBrace() .CloseBrace() ; construct .SummaryOpen() .Summary($"Create ElementDefinitionNode for child of common/primitive Fhir data type elements") .SummaryClose() .AppendCode($"public ElementDefinitionNode FindCommonChild(String parentPath, String childName)") .OpenBrace() .AppendCode($"switch (this.FhirItemType.FriendlyName())") .OpenBrace() ; GenerateFindCommonChild(construct, methods, FHIRAllTypes.Ratio); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Period); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Range); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Attachment); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Identifier); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Annotation); GenerateFindCommonChild(construct, methods, FHIRAllTypes.HumanName); GenerateFindCommonChild(construct, methods, FHIRAllTypes.CodeableConcept); GenerateFindCommonChild(construct, methods, FHIRAllTypes.ContactPoint); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Coding); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Money); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Address); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Timing); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Quantity); GenerateFindCommonChild(construct, methods, FHIRAllTypes.SampledData); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Signature); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Age); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Distance); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Duration); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Count); #if FHIR_R4 GenerateFindCommonChild(construct, methods, FHIRAllTypes.MoneyQuantity); #endif GenerateFindCommonChild(construct, methods, FHIRAllTypes.SimpleQuantity); GenerateFindCommonChild(construct, methods, FHIRAllTypes.Extension); construct .AppendCode($"default: return null;") .CloseBrace() .CloseBrace() ; #if FHIR_R3 String outputPath = Path.Combine(DirHelper.FindParentDir("Tools"), "FhirKhit.Tools.R3", "ElementDefinitionNode.FindChild.cs"); #elif FHIR_R4 String outputPath = Path.Combine(DirHelper.FindParentDir("Tools"), "FhirKhit.Tools.R4", "ElementDefinitionNode.FindChild.cs"); #endif editor.Save(outputPath); }
void GenerateFindCommonChild(CodeBlockNested construct, CodeBlockNested methods, FHIRAllTypes fhirType) { String fhirTypeName = ModelInfo.FhirTypeToFhirTypeName(fhirType); Type fhirCSType = ModelInfo.GetTypeForFhirType(fhirTypeName); String methodName = $"FindChild{fhirTypeName}"; construct .AppendCode($"case \"{fhirCSType.FriendlyName()}\": return {methodName}(parentPath, childName);") ; methods .BlankLine() .SummaryOpen() .Summary($"Manually add the children of a Coding element.") .SummaryClose() .AppendCode($"ElementDefinitionNode {methodName}(String parentPath, String childName)") .OpenBrace() .AppendCode($"ElementDefinitionNode retVal;") .AppendCode($"switch (childName)") .OpenBrace() ; foreach (PropertyInfo pi in fhirCSType.GetProperties()) { FhirElementAttribute attribute = pi.GetCustomAttribute <FhirElementAttribute>(); if (attribute != null) { String min; String max; if (pi.PropertyType.IsList()) { min = "0"; max = "*"; } if (pi.PropertyType.IsNullable()) { min = "0"; max = "1"; } else { min = "1"; max = "1"; } String varName = $"{attribute.Name}Var"; methods .AppendCode($"case \"{attribute.Name}\":") .OpenBrace() .AppendCode($"ElementDefinition e = new ElementDefinition") .OpenBrace() .AppendCode($"Path = $\"{{parentPath}}.{attribute.Name}\",") .AppendCode($"Short = \"{fhirType}.{attribute.Name} common attribute\",") .AppendCode($"Min = {min},") .AppendCode($"Max = \"{max}\"") .CloseBrace(";") .AppendCode($"retVal = new ElementDefinitionNode(this, e, typeof({pi.PropertyType.FriendlyName()}), nameof({fhirCSType.FriendlyName()}.{pi.Name}));") .AppendCode($"retVal.AutoGeneratedFlag = true;") .AppendCode($"break;") .CloseBrace(";") ; } } methods .AppendCode($"default: return null;") .CloseBrace() .AppendCode($"this.childNodeDictionary.Add(childName, retVal);") .AppendCode($"return retVal;") .CloseBrace() ; }
protected override void BuildItemWrite(CodeBlockNested b) { b .AppendCode($"{this.FhirClassName} retVal = this.Value.WriteMember(doc);") ; }
protected override void BuildItemRead(CodeBlockNested b) { b .AppendCode($"this.Value = ({this.ElementGetName}) item.Value;") ; }
void FixElement(CodeBlockNested code, PropertyInfo pi, String varName, String retValName) { //FhirElementAttribute attribute = pi.GetCustomAttribute<FhirElementAttribute>(); //if (attribute == null) // return; //if (attribute.IsPrimitiveValue) //{ // FixElementPrimitive(code, pi, varName, attribute); // return; //} FhirElementAttribute attribute = pi.GetCustomAttribute <FhirElementAttribute>(); if (attribute == null) { return; } String name = pi.PropertyType.FriendlyName(); code.AppendCode($"block.AppendCode(\"// Set {pi.Name} of type {name})\");"); switch (name) { case "Element": case "Extension": case "ResourceReference": case "List<Extension>": case "List<Element>": case "List<ResourceReference>": return; case "bool?": code .AppendCode($"if ({varName}.{pi.Name}.HasValue == false)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else if ({varName}.{pi.Name}.Value == true)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = true;\");") .AppendCode($"else") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = false;\");") ; break; case "byte[]": code .AppendCode($"if ({varName}.{pi.Name} == null)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .OpenBrace() .AppendCode($"block.OpenBrace();") .AppendCode($"block.AppendCode($\"byte[] data = new byte[]\");") .AppendCode($"block.OpenBrace();") .AppendCode($"Int32 i = 0;") .AppendCode($"while (i < {varName}.{pi.Name}.Length)") .OpenBrace() .AppendCode($"Int32 j = 0;") .AppendCode($"StringBuilder sb = new StringBuilder();") .AppendCode($"while ((i < {varName}.{pi.Name}.Length) && (j < 32))") .OpenBrace() .AppendCode($"sb.Append($\"{{{varName}.{pi.Name}[i]}}\");") .AppendCode($"if (i < {varName}.{pi.Name}.Length - 1) sb.Append(\",\");") .AppendCode($"j += 1;") .AppendCode($"i += 1;") .CloseBrace() .AppendCode($"block.AppendCode($\"{{sb.ToString()}}\");") .CloseBrace() .AppendCode($"block.CloseBrace(\";\");") .AppendCode($"block.AppendCode($\"{retValName}.{pi.Name} = data;\");") .AppendCode($"block.CloseBrace(\";\");") .CloseBrace() ; break; case "DateTimeOffset?": code .AppendCode($"if ({varName}.{pi.Name}.HasValue == false)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .OpenBrace() .AppendCode($"DateTimeOffset x = {varName}.{pi.Name}.Value;") .AppendCode($"block") .AppendCode($" .AppendCode($\"{retValName}.{pi.Name} = new DateTimeOffset({{x.Year}}, {{x.Month}}, {{x.Day}},\")") .AppendCode($" .AppendCode($\" {{x.Hour}}, {{x.Minute}}, {{x.Second}}, {{x.Millisecond}},\")") .AppendCode($" .AppendCode($\" new TimeSpan({{x.Offset.Hours}}, {{x.Offset.Minutes}}, {{x.Offset.Seconds}}));\")") .AppendCode($";") .CloseBrace() ; break; case "decimal?": code .AppendCode($"if ({varName}.{pi.Name}.HasValue == false)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = new Nullable<decimal>((decimal) {{{varName}.{pi.Name}.Value}});\");") ; break; case "int?": code .AppendCode($"if ({varName}.{pi.Name}.HasValue == false)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = new Nullable<int>((int) {{{varName}.{pi.Name}.Value}});\");") ; break; case "string": code .AppendCode($"if ({varName}.{pi.Name} == null)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = \\\"{{{varName}.{pi.Name}}}\\\";\");") ; break; default: if (name.StartsWith("Code<")) { String enumName = pi.PropertyType.GenericTypeArguments[0].FriendlyName(); code .AppendCode($"if ({varName}.{pi.Name} == null)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = new {name}({enumName}.{{{varName}.{pi.Name}.Value}});\");") ; } else if (name.StartsWith("List<")) { String source = TempName(); String target = TempName(); Type listType = pi.PropertyType.GenericTypeArguments[0]; String listTypeName = listType.FriendlyName(); code .AppendCode($"if ({varName}.{pi.Name} == null)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .OpenBrace() .AppendCode($"block.AppendCode($\"{retValName}.{pi.Name} = new {name}();\");") .AppendCode($"foreach (var {source} in {varName}.{pi.Name})") .OpenBrace() .AppendCode($"block.OpenBrace();") .AppendCode($"block.AppendCode(\"var {target} = new {listTypeName}();\");") ; this.FixElements(code, listType, $"{source}", $"{target}"); code .AppendCode($"block.AppendCode($\"{retValName}.{pi.Name}.Add({target});\");") .AppendCode($"block.CloseBrace();") .CloseBrace() .CloseBrace() ; } else if (name.EndsWith("?")) { Type nullableType = pi.PropertyType.GenericTypeArguments[0]; String nullableTypeName = nullableType.FriendlyName(); if (nullableType.IsEnum) { code .AppendCode($"if ({varName}.{pi.Name}.HasValue == false)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = {nullableTypeName}.{{{varName}.{pi.Name}.Value}};\");") ; } else if (nullableType.IsClass) { code .AppendCode($"if ({varName}.{pi.Name}.HasValue == false)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .OpenBrace() .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = new {nullableTypeName}();\");") ; this.FixElements(code, nullableType, $"{varName}.{pi.Name}", $"{retValName}.{pi.Name}"); code .CloseBrace() ; } else { throw new NotImplementedException(); } } else { String typeName = pi.PropertyType.FriendlyName(); code .AppendCode($"if ({varName}.{pi.Name} == null)") .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = null;\");") .AppendCode($"else") .OpenBrace() .AppendCode($" block.AppendCode($\"{retValName}.{pi.Name} = new {typeName}();\");") ; this.FixElements(code, pi.PropertyType, $"{varName}.{pi.Name}", $"{retValName}.{pi.Name}"); code .CloseBrace() ; } return; } }
public void FhirConstructB() { CodeEditor editor = new CodeEditor(); CodeBlockNested main = editor.Blocks.AppendBlock(); main .AppendLine($"using System;") .AppendLine($"using System.Linq;") .AppendLine($"using System.Collections.Generic;") .AppendLine($"using System.Reflection;") .AppendLine($"using System.Text;") .AppendLine($"using FhirKhit.ProfGen.PGShared;") .AppendLine($"using FhirKhit.ProfGen.PGShared.CodeGen.CSApi.Extensions;") .AppendLine($"using FhirKhit.Tools;") .AppendLine($"using Hl7.Fhir.Introspection;") .AppendLine($"using Hl7.Fhir.Model;") .AppendLine($"using Hl7.Fhir.Support.Model;") .AppendLine($"using System.Diagnostics;") .AppendLine($"using Hl7.FhirPath;") .BlankLine() .AppendLine($"namespace FhirKhit.ProfGen.GenTests") .OpenBrace() .AppendCode($"public class FhirConstructUse") .OpenBrace() ; CodeBlockNested construct = main.AppendBlock(); CodeBlockNested methods = main.AppendBlock(); main .CloseBrace() .CloseBrace() ; construct .AppendLine($"/// <summary>") .AppendLine($"/// generate code for eqch fhri element. Makes sure it compiles.") .AppendLine($"/// </summary>") .AppendCode($"public void Use()") .OpenBrace() .BlankLine() ; Int32 varNum = 0; foreach (FHIRAllTypes fhirType in Enum.GetValues(typeof(FHIRAllTypes)).OfType <FHIRAllTypes>()) { Trace.WriteLine($"fhirType {fhirType}"); String fhirTypeName = ModelInfo.FhirTypeToFhirTypeName(fhirType); Type fhirCSType = ModelInfo.GetTypeForFhirType(fhirTypeName); if (ModelInfo.IsPrimitive(fhirType)) { String varName1 = $"x{++varNum}"; String methodName1 = $"Method{++varNum}"; construct .AppendCode($"{fhirCSType} {varName1} = {methodName1}();") ; Element fix = CreateFix(fhirCSType); FhirConstruct.Construct(methods, fix, methodName1, out String propertyType1); } else if (ModelInfo.IsDataType(fhirType)) { if (Ignore(fhirType) == false) { String varName1 = $"x{++varNum}"; String methodName1 = $"Method{++varNum}"; construct .AppendCode($"{fhirCSType} {varName1} = {methodName1}();") ; Element fix = CreateFix(fhirCSType); FhirConstruct.Construct(methods, fix, methodName1, out String propertyType1); } } } construct .CloseBrace() ; #if FHIR_R2 String outputPath = Path.Combine(DirHelper.FindParentDir("Projects"), "ProfGen", "R2", "FhirKhit.ProfGen.GenTests.R2", "Generated", "FhirConstructUse.cs"); #elif FHIR_R3 String outputPath = Path.Combine(DirHelper.FindParentDir("Projects"), "ProfGen", "R3", "FhirKhit.ProfGen.GenTests.R3", "Generated", "FhirConstructUse.cs"); #elif FHIR_R4 String outputPath = Path.Combine(DirHelper.FindParentDir("Projects"), "ProfGen", "R4", "FhirKhit.ProfGen.GenTests.R4", "Generated", "FhirConstructUse.cs"); #endif editor.Save(outputPath); }
void ProcessProperty(Int32 indent, CodeBlockNested classBlock, CodeBlockNested propertiesBlock, CodeBlockNested mapBlock, String entryPath) { const string fcn = "ProcessProperty"; ElementDefinition ed = this.elements[this.elementIndex++]; // Note: Currently we do not handle extensions. if (ed.Path.LastPathPart() == "extension") { return; } if (ed.Path.LastPathPart() == "modifierExtension") { return; } if (String.IsNullOrEmpty(ed.SliceName) == false) { this.gen.ConversionWarn(this.GetType().Name, fcn, "Ignoring slice"); } if (ed.Slicing != null) { this.gen.ConversionWarn(this.GetType().Name, fcn, "Ignoring slicing"); } //if (ed.Fixed != null) // throw new ConvertErrorException(this.GetType().Name, fcn, $"Unexpected Fixed in element {ed.Path}."); //if (ed.Pattern != null) // throw new ConvertErrorException(this.GetType().Name, fcn, $"Unexpected Pattern in element {ed.Path}."); String propertyName = this.gen.UniquePropertyName(ed, out bool createFlag); String fullPropertyName; String propertyPath = $"{entryPath}.{propertyName}"; if (this.ContainsType(ed, BackboneElementStr)) { if (ed.Type.Count != 1) { throw new ConvertErrorException(this.GetType().Name, fcn, $"Backbone element {ed.Path} has multiple types."); } if (this.HasChildren(ed) == false) { throw new ConvertErrorException(this.GetType().Name, fcn, $"Backbone element {ed.Path} has no children."); } fullPropertyName = this.ProcessSubEntry(indent + 1, ed.Path, propertyPath, "Group", "Group", BackboneElementStr, $"Group definition of {ed.Path}", null); } else if (this.ContainsType(ed, ElementStr)) { if (ed.Type.Count != 1) { throw new ConvertErrorException(this.GetType().Name, fcn, $"Element {ed.Path} has multiple types."); } if (this.HasChildren(ed) == false) { throw new ConvertErrorException(this.GetType().Name, fcn, $"Element {ed.Path} has no children."); } fullPropertyName = this.ProcessSubEntry(indent + 1, ed.Path, propertyPath, "Group", "Group", ElementStr, $"Group definition of {ed.Path}", null); } else { fullPropertyName = propertyName; if (createFlag) { if (this.gen.IsSliceField(ed.Path)) { propertiesBlock .BlankLine() .AppendLine($"// Entry definition of {ed.Path}") .AppendCode($"Group: {propertyName}Slices") .AppendCode($"Property: {propertyName}Slice 0..*") .BlankLine() .AppendCode($"Group: {propertyName}Slice") .AppendCode($"Property: {propertyName} 1..1") .BlankLine() .AppendCode($"Element: {propertyName}") ; } else { propertiesBlock .BlankLine() .AppendLine($"// Entry definition of {ed.Path}") .AppendCode($"Element: {propertyName}") ; } HashSet <String> outputTypes = new HashSet <string>(); bool firstFlag = true; void OutputType(String pType) { if (outputTypes.Contains(pType)) { return; } outputTypes.Add(pType); if (firstFlag) { propertiesBlock.AppendCode($"Value: {pType}"); } else { propertiesBlock.AppendCode($" or {pType}"); } firstFlag = false; } { String basePropertyPath = propertyPath.SkipFirstPathPart(); String baseEdPath = ed.Path.SkipFirstPathPart(); if (this.gen.IsSliceField(ed.Path)) { mapBlock.AppendCode($" {basePropertyPath}Slices.{basePropertyPath}Slice.{basePropertyPath} maps to {baseEdPath}"); } else { mapBlock.AppendCode($" {basePropertyPath} maps to {baseEdPath}"); } } foreach (ElementDefinition.TypeRefComponent type in ed.Type) { switch (type.Code) { case null: break; case "boolean": case "integer": case "decimal": case "uri": case "string": case "base64Binary": case "instant": case "date": case "dateTime": case "time": case "oid": case "id": case "markdown": case "unsignedInt": case "positiveInt": case "xhtml": OutputType($"{type.Code}"); break; case "CodeableConcept": case "Coding": case "code": OutputType($"concept"); break; case "uuid": case "url": case "canonical": OutputType($"uri"); break; case "Reference": if (type.Profile.Any()) { throw new ConvertErrorException(this.GetType().Name, fcn, $"Unexpected profile in type {ed.Path}:{type.Code}."); } if (type.TargetProfile.Count() == 0) { this.gen.AddAbbreviatedResource(ResourceStr); OutputType($"Resource"); } else { foreach (string target in type.TargetProfile) { this.gen.AddAbbreviatedResource(target); String targetEntryName = target.LastUriPart().ToMachineName(); OutputType($"{targetEntryName}"); } } break; default: if (type.Profile.Count() == 0) { this.gen.AddAbbreviatedResource(type.Code); OutputType($"{type.Code.ToMachineName()}"); } else { foreach (string profile in type.Profile) { this.gen.AddAbbreviatedResource(profile); String profileName = profile.LastUriPart().ToMachineName(); OutputType($"{profileName}"); } } break; } } } } if (this.gen.IsSliceField(ed.Path)) { classBlock.AppendCode($"Property: {fullPropertyName}Slices 0..1"); } else { classBlock.AppendCode($"Property: {fullPropertyName} {ed.Min}..{ed.Max}"); } }
/// <summary> /// Create new file containing the definition for the new item. /// The new entry is created in its own namespace. /// </summary> String ProcessSubEntry(Int32 indent, String elementPath, String entryPath, String suffix, String typeName, String parent, String description, String comment) { CodeBlockNested classBlock = entryEditor.Blocks.AppendBlock(); CodeBlockNested propertydefinitionsBlock = entryEditor.Blocks.AppendBlock(); CodeBlockNested mapBlock = mapEditor.Blocks.AppendBlock(); String entryName = this.gen.GetFieldMap(elementPath); if (String.IsNullOrEmpty(suffix) == false) { entryName += suffix; entryPath += suffix; } classBlock .BlankLine() .AppendComment(comment) .AppendCode($"{typeName}: {entryName}") ; if (indent == 0) { mapBlock .BlankLine() .AppendCode($"{entryPath} maps to {elementPath}:") ; } else { mapBlock .BlankLine() .AppendCode($" {entryPath.SkipFirstPathPart()} maps to {elementPath.SkipFirstPathPart()}") ; } switch (parent) { case null: case ResourceStr: case BackboneElementStr: case ElementStr: break; default: this.gen.AddResource(parent); classBlock.AppendCode($"Parent: {parent}"); break; } classBlock .AppendCode($"Description: \"{description}\"") ; while (this.elementIndex < this.elements.Length) { ElementDefinition subElement = this.elements[this.elementIndex]; if (subElement.Path.StartsWith($"{elementPath}.") == false) { break; } this.ProcessProperty(indent, classBlock, propertydefinitionsBlock, mapBlock, entryPath); } return(entryName); }
//$$$ Delete /// <summary> /// Traverse children using simple fhir path query. /// Return selected elements, or null if not found. /// </summary> public bool Generate(CodeBlockNested block, String methodModifiers, String methodName, ElementDefinitionNode node, String path, out Type leafType) { const String fcn = nameof(Generate); if (block is null) { throw new ArgumentNullException(nameof(block)); } if (methodModifiers is null) { throw new ArgumentNullException(nameof(methodModifiers)); } if (methodName is null) { throw new ArgumentNullException(nameof(methodName)); } if (node is null) { throw new ArgumentNullException(nameof(node)); } if (path is null) { throw new ArgumentNullException(nameof(path)); } //String fhirTypeName = node.FhirType.FriendlyName(); String fhirItemTypeName = node.FhirItemType.FriendlyName(); // we need to write header after we determine leaf node type. CodeBlockNested methodHeaderBlock = block.AppendBlock(); block .OpenBrace() .BlankLine() ; childBlock = block.AppendBlock(); String[] pathItems = path.Split('.'); Int32 i = 0; if (pathItems[0] == node.Name) { i += 1; } Int32 resultCounter = 0; leafType = null; String resultThis = "head"; while (i < pathItems.Length) { resultCounter += 1; String resultNext = $"result{resultCounter}"; String pathItem = pathItems[i++]; if (pathItem.StartsWith("resolve(")) { this.gen.ConversionError(this.GetType().Name, fcn, $"TODO: FhirPath operator {pathItem} not implemented"); return(false); } else if (pathItem.StartsWith("extension(\"")) { this.gen.ConversionError(this.GetType().Name, fcn, $"TODO: FhirPath operator {pathItem} not implemented"); return(false); } else if (pathItem.StartsWith("ofType(")) { this.gen.ConversionError(this.GetType().Name, fcn, $"TODO: FhirPath operator {pathItem} not implemented"); return(false); } else { if (node.TryGetAnyChild(pathItem, out ElementDefinitionNode next) == false) { this.gen.ConversionError(this.GetType().Name, fcn, $"Child {pathItem} not found"); return(false); } Type nodeType = node.FhirItemType; PropertyInfo childProperty = nodeType.GetPropertyByFhirName(pathItem); String childPropertyName = childProperty.Name; String childMethodName = GenerateGetChild(childPropertyName, nodeType, childProperty.PropertyType); block.AppendCode($"IEnumerable<{next.FhirItemType.FriendlyName()}> {resultNext} = {childMethodName}({resultThis});"); resultThis = resultNext; node = next; } } block .AppendCode($"return {resultThis};") .CloseBrace() ; leafType = node.FhirItemType; methodHeaderBlock .AppendCode($"{methodModifiers} IEnumerable<{leafType.FriendlyName()}> {methodName}(IEnumerable<{fhirItemTypeName}> head)") ; return(true); }
void ProcessFhirResource(SDefInfo sDefInfo) { const String fcn = "ProcessFhirResource"; StructureDefinition sDef = sDefInfo.SDef; ClearSDef(sDef); this.ConversionInfo(this.GetType().Name, fcn, $"Processing Resource {sDef.Name} {sDefInfo.TFlag}"); String instanceName = ResourceName(sDef.Name); CodeEditor instanceEditor = this.project.CreateEditor(Path.Combine(ResourceGenPath, $"{instanceName}.cs")); CodeBlockNested instanceBlock = instanceEditor.Blocks.AppendBlock(); instanceBlock .AppendCode("using System;") .AppendCode("using System.Diagnostics;") .AppendCode("using System.IO;") .AppendCode("using System.Linq;") .AppendCode("using Hl7.Fhir.Model;") .BlankLine() .AppendCode($"namespace {ResourceNameSpace}") .OpenBrace() .AppendCode("#region Json") .AppendCode("#if NEVER") .AppendLines("", sDef.ToFormatedJson().ToLines()) .AppendLine("#endif") .AppendCode("#endregion") .SummaryOpen() .Summary($"Fhir resource '{sDef.Name}'") .SummaryClose() .DefineBlock(out CodeBlockNested classBlock) .CloseBrace() ; String baseClass = ResourceBase; if (String.IsNullOrEmpty(sDef.BaseDefinition) == false) { String name = this.ResourceName(sDef.BaseDefinition.LastUriPart()); baseClass = $"{ResourceNameSpace}.{name}"; } Int32 i = 0; DefineClass(classBlock, sDef.Differential.Element.ToArray(), ref i, sDef.Differential.Element[0].Path, instanceName, baseClass, out CodeBlockNested constructorBlock); constructorBlock .AppendCode($"this.Name = \"{sDef.Name}\";") .AppendCode($"this.Uri = \"{sDef.Url}\";") ; if (i != sDef.Differential.Element.Count) { throw new ConvertErrorException(this.GetType().Name, fcn, $"Internal error. Invalid element index"); } }
void DefineClassFields( CodeBlockNested subClassBlock, CodeBlockNested fieldsBlock, CodeBlockNested constructorBlock, CodeBlockNested writeBlock, ElementDefinition[] elements, String basePath, String className, ref Int32 index) { while (index < elements.Length) { ElementDefinition ed = elements[index]; // We know when we are at the end of a sub class, when the // path does no longer start with subPath. String path = ed.Path; if (ed.Path.StartsWith(basePath) == false) { return; } path = path.Substring(basePath.Length); String elementName = ElementName(ed.Path.LastPathPart()); fieldsBlock .AppendComment($"{index}. {elements[index].Path}") .AppendCode($"public ElementDefinitionInfo {elementName};") ; writeBlock .AppendCode($"{elementName}.Write(sDef);") ; Int32 min = 0; Int32 max = -1; if (ed.Min.HasValue) { min = ed.Min.Value; } if ((String.IsNullOrEmpty(ed.Max) == false) && (ed.Max != "*")) { max = Int32.Parse(ed.Max); } constructorBlock .OpenBrace() .AppendComment($"{index}. {elements[index].Path}") .AppendCode($"this.{elementName} = new ElementDefinitionInfo") .OpenBrace() .AppendCode($"Name = \"{elementName}\",") .AppendCode($"Path= \"{ed.Path}\",") .AppendCode($"Id = \"{ed.ElementId}\",") .AppendCode($"Min = {min},") .AppendCode($"Max = {max},") .AppendCode($"Types = new BaseType[]") .OpenBrace() .DefineBlock(out CodeBlockNested typesBlock) .CloseBrace("") .CloseBrace(";") .CloseBrace("") ; // If next elements starts with this items path, then this is a // subelement, so start creating sub class. if ( (index < elements.Length - 1) && (elements[index + 1].Path.StartsWith($"{ed.Path}.")) ) { String subClassName = TypeName(path.LastPathPart()); typesBlock .AppendCode($"new {subClassName}") .OpenBrace() .CloseBrace() ; DefineClass(subClassBlock, elements, ref index, ed.Path, subClassName, ComplexBase, out CodeBlockNested dummy); } else { for (Int32 typeIndex = 0; typeIndex < ed.Type.Count; typeIndex += 1) { String sep = typeIndex == (ed.Type.Count - 1) ? "" : ","; ElementDefinition.TypeRefComponent type = ed.Type[typeIndex]; switch (type.Code) { case null: break; case "boolean": case "integer": case "decimal": case "uri": case "string": case "base64Binary": case "instant": case "date": case "dateTime": case "time": case "oid": case "id": case "markdown": case "unsignedInt": case "positiveInt": case "xhtml": case "code": case "uuid": case "url": case "canonical": { String sep1 = ""; typesBlock .AppendCode($"new {PrimitiveNameSpace}.{PrimitiveName(type.Code)}") .OpenBrace() .AppendProfiles(type.Profile, ref sep1) .AppendTargetProfiles(type.TargetProfile, ref sep1) .CloseBrace(sep) ; } sep = ", "; break; case "CodeableConcept": case "Coding": { String sep1 = ""; typesBlock .AppendCode($"new {ComplexNameSpace}.{TypeName(type.Code)}") .OpenBrace() .AppendProfiles(type.Profile, ref sep1) .AppendTargetProfiles(type.TargetProfile, ref sep1) .CloseBrace(sep) ; } sep = ", "; break; case "Resource": { String sep1 = ""; typesBlock .AppendCode($"new {ResourceNameSpace}.{ResourceName(type.Code)}") .OpenBrace() .AppendProfiles(type.Profile, ref sep1) .AppendTargetProfiles(type.TargetProfile, ref sep1) .CloseBrace(sep) ; } sep = ", "; break; case "Reference": { String sep1 = ""; typesBlock .AppendCode($"new {ComplexNameSpace}.{TypeName(type.Code)}") .OpenBrace() .AppendProfiles(type.Profile, ref sep1) .AppendTargetProfiles(type.TargetProfile, ref sep1) .CloseBrace(sep) ; } sep = ", "; break; default: typesBlock .AppendCode($"new {ComplexNameSpace}.{TypeName(type.Code)}") .OpenBrace() .CloseBrace(sep) ; sep = ", "; break; } } index += 1; } } }