SliceAccessorTypes SliceAccessorType(ElementDefinitionNode sliceNode) { const String fcn = nameof(SliceAccessorType); String baseType = sliceNode.FhirType.FriendlyName(); if (sliceNode.IsListType) { switch (sliceNode.Element.Max.ToMax()) { case 0: this.gen.ConversionError(this.GetType().Name, fcn, $"Slice node '{sliceNode.FullPath()}' has max of 0. Not sure what I am supposed to do!"); return(SliceAccessorTypes.Error); case 1: return(SliceAccessorTypes.Single); default: return(SliceAccessorTypes.Multiple); } } else { this.gen.ConversionError(this.GetType().Name, fcn, $"Slice node '{sliceNode.FullPath()}' unknown base type {baseType}"); return(SliceAccessorTypes.Error); } }
Element GetItem(ElementDefinitionNode sliceNode, String path) { const String fcn = nameof(GetItem); String[] pathParts = path.Split('.'); ElementDefinitionNode node = sliceNode; for (Int32 i = 0; i < pathParts.Length; i++) { String pathPart = pathParts[i]; ElementDefinitionNode next = null; foreach (ElementDefinitionNode n in node.ChildNodes) { if (n.Name == pathPart) { next = n; break; } } if (next == null) { this.Gen.ConversionError(this.GetType().Name, fcn, $"Cant find child node '{pathPart}' in '{sliceNode.FullPath()}'"); return(null); } node = next; } return(node.Element.Fixed); }
public CSSliceCreator(String className, CSCodeFormatter csCode, CodeBlockNested subClassBlock, CodeBlockNested methodsBlock, ElementDefinitionNode elementNode, Type fhirBaseClassType) { if (subClassBlock is null) { throw new ArgumentNullException(nameof(subClassBlock)); } if (methodsBlock is null) { throw new ArgumentNullException(nameof(methodsBlock)); } if (elementNode is null) { throw new ArgumentNullException(nameof(elementNode)); } this.csCode = csCode; if (elementNode is null) { throw new ArgumentNullException(nameof(elementNode)); } this.className = className; this.subClassBlock = subClassBlock.AppendBlock(); this.methodsBlock = methodsBlock.AppendBlock(); this.elementNode = elementNode; this.fhirBaseClassType = fhirBaseClassType; }
public GenerateItem(SliceGenerator gen, String nameSpace, String className, String profileType, Type fhirType, OutputLanguageType outputLanguage, ElementDefinitionNode profileElements) { this.gen = gen; this.nameSpace = nameSpace; this.className = className; this.fhirType = fhirType; this.outputLanguage = outputLanguage; this.profileElements = profileElements; this.profileType = profileType; }
bool CreateSlice(ElementDefinitionNode elementNode) { const String fcn = nameof(ProcessResourceConstraint); this.gen.ConversionInfo(this.GetType().Name, fcn, $"Processing slice on node {elementNode.Element.Path}"); List <ElementDefinition.DiscriminatorComponent> d = elementNode.Element.Slicing?.Discriminator; if ((d == null) || (d.Count == 0)) { this.gen.ConversionError(this.GetType().Name, fcn, $"Slice of element {elementNode.Element.Path} failed. Element lacks slice discriminator"); return(false); } return(this.Code.CreateSlice(elementNode)); }
void ProcessProfile(StructureDefinition profile) { String fcn = nameof(ProcessProfile); try { if (profile.Snapshot == null) { SnapshotCreator.Create(profile); } ElementDefinitionNode profileItems = ElementDefinitionNode.Create(profile); String baseFhirResourceName = profile.Type; Type fhirType = ModelInfo.GetTypeForFhirType(baseFhirResourceName); if (fhirType == null) { throw new Exception($"Can not find Fhir Resource Class for '{baseFhirResourceName}'"); } String name = $"{profile.Name}Extensions"; GenerateItem gi = new GenerateItem(this, this.NameSpace, name, profile.Type, fhirType, this.OutputLanguage, profileItems); gi.Process(); if (this.SaveFlag == true) { String outputFile = Path.Combine(this.OutputDir, $"{name}.cs"); File.WriteAllText(outputFile, gi.GetCode()); } } catch (Exception err) { this.ConversionError(this.GetType().Name, fcn, $"Profile {profile.Name} failed with exception {err.Message}"); } }
void CreateSliceAccessor(ElementDefinitionNode sliceNode, String sliceClassName, String sliceInterfaceName) { String sliceName = sliceNode.Element.SliceName.ToMachineName(); String propertyPath = sliceNode.PropertyName; String sliceBaseClassName; switch (SliceAccessorType(sliceNode)) { case SliceAccessorTypes.Error: sliceBaseClassName = $"??"; break; case SliceAccessorTypes.Single: sliceBaseClassName = $"ISliceAccessorSingle<{accessorType}>"; break; case SliceAccessorTypes.Multiple: sliceBaseClassName = $"ISliceAccessorMultiple<{accessorType}>"; break; default: throw new NotImplementedException("Unknown SliceAccessorTypes value"); } this.methodsBlock .BlankLine() .SummaryOpen() .Summary($"Extension method to return slice {sliceName} on {elementNode.Name}") .SummaryClose() .Example( $"{this.fhirBaseClassType.FriendlyName()} resource = new {this.fhirBaseClassType.FriendlyName()}();", $"{this.className}.{sliceInterfaceName} sliceAccessor = resource.{propertyPath}.{sliceName}();" ) .AppendCode($"public static {sliceInterfaceName} {sliceName}(this {baseTypeName} item)") .OpenBrace() .AppendCode($"{sliceClassName} retVal = new {sliceClassName}(item);") .AppendCode($"return retVal;") .CloseBrace() ; }
bool DefineDiscriminator(Int32 index, CodeBlockNested sliceDiscriminators, ElementDefinitionNode sliceNode, String varName, ElementDefinition.DiscriminatorComponent discriminator) { const String fcn = nameof(DefineDiscriminator); switch (discriminator.Type) { case ElementDefinition.DiscriminatorType.Value: if (DefineSliceOnValueDiscriminator(index, sliceDiscriminators, sliceNode, varName, discriminator, "valueFilterMethod", "leafType") == false) { return(false); } return(true); default: this.gen.ConversionError(this.GetType().Name, fcn, $"TODO: discriminator.Type {discriminator.Type} currently implemented. '{elementNode.FullPath()}'"); return(false); } }
/// <summary> /// Process elements, searching for elements that are sliced. /// </summary> /// <param name="sd"></param> /// <returns></returns> bool ProcessElementSlices(ElementDefinitionNode node) { //const String fcn = nameof(ProcessResourceConstraint); // Dont create slices on extensions. Those will be handled by another tool. if (node.FhirItemType == typeof(Extension)) { return(false); } bool retVal = true; if (node.Slices.Any()) { if (CreateSlice(node) == false) { retVal = false; } } foreach (var child in node.ChildNodes) { if (ProcessElementSlices(child) == false) { retVal = false; } } foreach (var slice in node.Slices) { if (ProcessElementSlices(slice) == false) { retVal = false; } } return(retVal); }
/// <summary> /// Create method to set element at indicated path to passed value. /// </summary> public bool GenerateSetElements(CodeBlockNested block, ElementDefinitionNode node, String baseName, String path, String leafNodeValue = null) { const String fcn = nameof(GenerateSetElements); CodeBlockNested childBlock; if (block is null) { throw new ArgumentNullException(nameof(block)); } if (node is null) { throw new ArgumentNullException(nameof(node)); } if (path is null) { throw new ArgumentNullException(nameof(path)); } void GenerateSetChildCode(ref string containerName, Type containerType, string itemName, Type itemType, String value) { if (containerName is null) { throw new ArgumentNullException(nameof(containerName)); } if (itemName is null) { throw new ArgumentNullException(nameof(itemName)); } String containerTypeName = containerType.FriendlyName(); String itemTypeName = itemType.FriendlyName(); if (value == null) { value = $"new {itemTypeName}()"; } if (containerType.IsList()) { childBlock .AppendCode($"if ({containerName}.Count == 0)") .AppendCode($" {containerName}.Add({value});") ; containerName = $"{containerName}[0].{itemName}"; } else { childBlock .AppendCode($"if ({containerName}.{itemName} == null)") .AppendCode($" {containerName}.{itemName} = {value};") ; containerName = $"{containerName}.{itemName}"; } } String[] pathItems = path.Split('.'); Int32 i = 0; if (pathItems[0] == node.Name) { i += 1; } childBlock = block.AppendBlock(); String fullName = baseName; while (i < pathItems.Length) { 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); } PropertyInfo childProperty = node.FhirItemType.GetPropertyByFhirName(pathItem); String value = null; if (i == pathItems.Length - 1) { value = leafNodeValue; } GenerateSetChildCode(ref fullName, node.FhirItemType, childProperty.Name, next.FhirType, value); node = next; } i += 1; } return(true); }
/// <summary> /// Create slices on indicated node. /// </summary> /// <param name="elementNode">Element node containing discriminator</param> /// <returns></returns> public bool CreateSlice(ElementDefinitionNode elementNode) { CSSliceCreator c = new CSSliceCreator(this.className, this, this.subClassBlock, this.methodsBlock, elementNode, this.fhirBaseClassType); return(c.CreateSlice()); }
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() ; }
//$$$ 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); }
public SDefInfo(StructureDefinition sDef) { this.SDef = sDef; ElementDefinitionNode = ElementDefinitionNode.Create(sDef); }