diff --git a/.run/firely-all.run.xml b/.run/firely-all.run.xml
new file mode 100644
index 000000000..7f737b051
--- /dev/null
+++ b/.run/firely-all.run.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.Health.Fhir.CodeGen/FhirExtensions/ElementDefinitionExtensions.cs b/src/Microsoft.Health.Fhir.CodeGen/FhirExtensions/ElementDefinitionExtensions.cs
index 2bba61f4e..aba2ec179 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/FhirExtensions/ElementDefinitionExtensions.cs
+++ b/src/Microsoft.Health.Fhir.CodeGen/FhirExtensions/ElementDefinitionExtensions.cs
@@ -78,7 +78,7 @@ public static int cgComponentFieldOrder(this ElementDefinition ed)
public static string cgBasePath(this ElementDefinition ed) => ed.Base?.Path ?? string.Empty;
/// Gets the explicit name of this element if set, or returns an empty string.
- public static string cgExplicitName(this ElementDefinition ed) => ed.GetExtensionValue(CommonDefinitions.ExtUrlExplicitTypeName)?.ToString() ?? string.Empty;
+ public static string? cgExplicitName(this ElementDefinition ed) => ed.GetExtensionValue(CommonDefinitions.ExtUrlExplicitTypeName)?.ToString();
/// Gets the short name of this element, or explicit name if there is one.
/// The ed to act on.
@@ -89,12 +89,10 @@ public static string cgName(this ElementDefinition ed, bool allowExplicitName =
{
if (allowExplicitName)
{
- string en = ed.cgExplicitName();
+ string? en = ed.cgExplicitName();
- if (!string.IsNullOrEmpty(en))
- {
+ if (en is not null)
return en;
- }
}
if (removeChoiceMarker && ed.Path.EndsWith("[x]", StringComparison.Ordinal))
diff --git a/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/CSharpFirely2.cs b/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/CSharpFirely2.cs
index dd4d280db..6bdca6cae 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/CSharpFirely2.cs
+++ b/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/CSharpFirely2.cs
@@ -4,16 +4,16 @@
//
using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Text;
using Hl7.Fhir.Model;
using Hl7.Fhir.Utility;
using Microsoft.Health.Fhir.CodeGen.FhirExtensions;
using Microsoft.Health.Fhir.CodeGen.Models;
using Microsoft.Health.Fhir.CodeGen.Utils;
using Microsoft.Health.Fhir.CodeGenCommon.FhirExtensions;
+using Microsoft.Health.Fhir.CodeGenCommon.Models;
using Microsoft.Health.Fhir.CodeGenCommon.Packaging;
using Microsoft.Health.Fhir.CodeGenCommon.Utils;
+using Ncqa.Cql.Model;
using static Microsoft.Health.Fhir.CodeGen.Language.Firely.CSharpFirelyCommon;
using static Microsoft.Health.Fhir.CodeGenCommon.Extensions.FhirNameConventionExtensions;
@@ -32,7 +32,7 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
set => _generateHashesInsteadOfOutput = value;
}
- private Dictionary _fileHashes = [];
+ private readonly Dictionary _fileHashes = [];
Dictionary IFileHashTestable.FileHashes => _fileHashes;
/// (Immutable) Name of the language.
@@ -43,9 +43,6 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
public Type ConfigType => typeof(FirelyGenOptions);
- /// Gets the FHIR primitive type map.
- public Dictionary FhirPrimitiveTypeMap => CSharpFirelyCommon.PrimitiveTypeMap;
-
/// Gets a value indicating whether this language is idempotent.
public bool IsIdempotent => false;
@@ -74,7 +71,7 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
private string _exportDirectory = string.Empty;
/// Structures to skip generating.
- internal static readonly HashSet _exclusionSet =
+ internal static readonly HashSet ExclusionSet =
[
/* These two types are generated as interfaces, so require special handling and
* are not to be treated as normal resources. */
@@ -100,15 +97,30 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
"http://hl7.org/fhir/ValueSet/mimetypes",
];
+ private static readonly Dictionary<(string,string), VersionIndependentResourceTypesAll> _searchParamDefsTargetRemovals = new()
+ {
+ [("DiagnosticReport", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("RiskAssessment", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("List", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("VisionPrescription", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("ServiceRequest", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("Flag", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("Observation", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("NutritionOrder", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("Composition", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("DeviceRequest", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ [("Procedure", "encounter")] = VersionIndependentResourceTypesAll.EpisodeOfCare,
+ };
+
///
/// List of types introduced in R5 that are retrospectively introduced in R3 and R4.
///
- internal static readonly List _sharedR5DataTypes =
+ internal static readonly List SharedR5DataTypes =
[
- new WrittenModelInfo { CsName = "BackboneType", FhirName = "BackboneType", IsAbstract = true },
- new WrittenModelInfo { CsName = "Base", FhirName = "Base", IsAbstract = true },
- new WrittenModelInfo { CsName = "DataType", FhirName = "DataType", IsAbstract = true },
- new WrittenModelInfo { CsName = "PrimitiveType", FhirName = "PrimitiveType", IsAbstract = true },
+ new("BackboneType", "BackboneType", true),
+ new("Base", "Base",true),
+ new("DataType", "DataType", true),
+ new("PrimitiveType", "PrimitiveType", true),
];
///
@@ -116,6 +128,7 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
///
private static readonly List _baseSubsetComplexTypes =
[
+ "Address",
"Attachment",
"BackboneElement",
"BackboneType",
@@ -125,8 +138,10 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
"ContactPoint",
"ContactDetail",
"DataType",
+ "Duration",
"Element",
"Extension",
+ "HumanName",
"Identifier",
"Meta",
"Narrative",
@@ -134,6 +149,7 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
"PrimitiveType",
"Quantity",
"Range",
+ "Ratio",
"Reference",
"Signature",
"UsageContext",
@@ -182,10 +198,9 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
[
"http://hl7.org/fhir/ValueSet/publication-status",
"http://hl7.org/fhir/ValueSet/FHIR-version",
-
- // Doesn't strictly need to be in base (but in conformance),
- // but we used to generate it for base, so I am keeping it that way.
+ "http://hl7.org/fhir/ValueSet/search-param-type",
"http://hl7.org/fhir/ValueSet/filter-operator",
+ "http://hl7.org/fhir/ValueSet/version-independent-all-resource-types"
];
///
@@ -195,7 +210,6 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
[
"http://hl7.org/fhir/ValueSet/capability-statement-kind",
"http://hl7.org/fhir/ValueSet/binding-strength",
- "http://hl7.org/fhir/ValueSet/search-param-type",
// These are necessary for CapabilityStatement/CapabilityStatement2
// CapStat2 has disappeared in ballot, if that becomes final,
@@ -212,38 +226,10 @@ bool IFileHashTestable.GenerateHashesInsteadOfOutput
"http://hl7.org/fhir/ValueSet/codesystem-content-mode"
];
- ///
- /// Information about special handling for specific value sets (for backwards compatibility of
- /// generated code)
- ///
- internal record class ValueSetBehaviorOverrides
- {
- public required bool AllowShared { get; init; }
- public required bool AllowInClasses { get; init; }
- public HashSet ForceInClasses { get; init; } = [];
- }
-
- ///
- /// (Immutable) ValueSets that need special handling for backwards compatibility
- ///
- ///
- /// Plan to remove in SDK v6.0
- ///
- private static readonly Dictionary _valueSetBehaviorOverrides = new()
- {
- { "http://hl7.org/fhir/ValueSet/consent-data-meaning", new ValueSetBehaviorOverrides() { AllowShared = true, AllowInClasses = true, } },
- { "http://hl7.org/fhir/ValueSet/consent-provision-type", new ValueSetBehaviorOverrides() { AllowShared = true, AllowInClasses = true, }},
- { "http://hl7.org/fhir/ValueSet/encounter-status", new ValueSetBehaviorOverrides() { AllowShared = true, AllowInClasses = true, }},
- { "http://hl7.org/fhir/ValueSet/list-mode", new ValueSetBehaviorOverrides() { AllowShared = true, AllowInClasses = true, }},
- { "http://hl7.org/fhir/ValueSet/color-codes", new ValueSetBehaviorOverrides() { AllowShared = false, AllowInClasses = false, }},
- //{ "http://hl7.org/fhir/ValueSet/timezones", new ValueSetBehaviorOverrides() { AllowShared = true, ForceInClasses = ["Appointment"] }},
- //{ "http://hl7.org/fhir/ValueSet/timezones", new ValueSetBehaviorOverrides() { AllowShared = true, ForceInClasses = ["Appointment"] }},
- };
-
///
/// List of all valuesets that we should publish as a shared Enum although there is only 1 reference to it.
///
- internal static readonly List<(string, string)> _explicitSharedValueSets =
+ internal static readonly List<(string, string)> ExplicitSharedValueSets =
[
// This enum should go to Template-binding.cs because otherwise it will introduce a breaking change.
("R4", "http://hl7.org/fhir/ValueSet/messageheader-response-request"),
@@ -253,14 +239,13 @@ internal record class ValueSetBehaviorOverrides
("R5", "http://hl7.org/fhir/ValueSet/constraint-severity"),
];
-
/// Gets the reserved words.
/// The reserved words.
private static readonly HashSet _reservedWords = [];
- private static readonly Func SupportedResourcesFilter = wmi => !wmi.IsAbstract;
- private static readonly Func FhirToCsFilter = wmi => !_excludeFromCsToFhir!.Contains(wmi.FhirName);
- private static readonly Func CsToStringFilter = FhirToCsFilter;
+ private static readonly Func _supportedResourcesFilter = wmi => !wmi.IsAbstract;
+ private static readonly Func _fhirToCsFilter = wmi => !_excludeFromCsToFhir!.Contains(wmi.FhirName);
+ private static readonly Func _csToStringFilter = _fhirToCsFilter;
private static readonly string[] _excludeFromCsToFhir =
[
@@ -281,7 +266,7 @@ internal record class ValueSetBehaviorOverrides
/// Some valuesets have names that are the same as element names or are just not nice - use this collection
/// to change the name of the generated enum as required.
///
- internal static readonly Dictionary _enumNamesOverride = new()
+ internal static readonly Dictionary EnumNamesOverride = new()
{
["http://hl7.org/fhir/ValueSet/characteristic-combination"] = "CharacteristicCombinationCode",
["http://hl7.org/fhir/ValueSet/claim-use"] = "ClaimUseCode",
@@ -295,7 +280,75 @@ internal record class ValueSetBehaviorOverrides
["http://hl7.org/fhir/ValueSet/fhir-types"] = "FHIRAllTypes"
};
- private record SinceVersion(FhirReleases.FhirSequenceCodes Since);
+ private record ElementTypeChange(FhirReleases.FhirSequenceCodes Since,
+ TypeReference DeclaredTypeReference);
+
+ private static readonly ElementTypeChange[] _stringToMarkdown =
+ [
+ new(FhirReleases.FhirSequenceCodes.STU3, PrimitiveTypeReference.String),
+ new(FhirReleases.FhirSequenceCodes.R5, PrimitiveTypeReference.Markdown)
+ ];
+
+ ///
+ /// Given one of the versions, returns a string that describes from which version until which
+ /// version that change was in effect.
+ ///
+ private static string VersionChangeMessage(ElementTypeChange[] changeSet, ElementTypeChange thisChange, bool capitalize)
+ {
+ int index = Array.IndexOf(changeSet, thisChange);
+ if (index == -1) throw new ArgumentException("Change needs to be part of the set", nameof(thisChange));
+
+ if (index + 1 >= changeSet.Length)
+ {
+ // This is the last change in the set
+ return $"{(capitalize ? "S" : "s")}tarting from {thisChange.Since}";
+ }
+
+ int now = (int)thisChange.Since;
+ int next = (int)changeSet[index + 1].Since;
+ IEnumerable versionOrdinals =
+ Enumerable.Range(now, next - now).Cast();
+ string versions = string.Join(", ", versionOrdinals.Select(v => v.ToString()));
+ versions = replaceLastOccurrence(versions, ", ", " and ");
+ return $"{(capitalize ? "I" : "i")}n {versions}";
+
+ static string replaceLastOccurrence(string source, string find, string replace)
+ {
+ int place = source.LastIndexOf(find, StringComparison.Ordinal);
+
+ if (place == -1)
+ return source;
+
+ return source.Remove(place, find.Length).Insert(place, replace);
+ }
+ }
+
+ // ReSharper disable ArrangeObjectCreationWhenTypeNotEvident
+ private static readonly Dictionary _elementTypeChanges = new()
+ {
+ ["Attachment.size"] = [
+ new(FhirReleases.FhirSequenceCodes.STU3, PrimitiveTypeReference.UnsignedInt),
+ new (FhirReleases.FhirSequenceCodes.R5, PrimitiveTypeReference.Integer64),
+ ],
+ ["Attachment.url"] = [
+ new(FhirReleases.FhirSequenceCodes.STU3, PrimitiveTypeReference.Uri),
+ new(FhirReleases.FhirSequenceCodes.R4, PrimitiveTypeReference.Url),
+ ],
+ ["Meta.profile"] = [
+ new(FhirReleases.FhirSequenceCodes.STU3, PrimitiveTypeReference.Uri),
+ new(FhirReleases.FhirSequenceCodes.R4, PrimitiveTypeReference.Canonical),
+ ],
+ ["Bundle.link.relation"] = [
+ new(FhirReleases.FhirSequenceCodes.STU3, PrimitiveTypeReference.String),
+ new(FhirReleases.FhirSequenceCodes.R5, PrimitiveTypeReference.Code)
+ ],
+
+ ["ElementDefinition.constraint.requirements"] = _stringToMarkdown,
+ ["ElementDefinition.binding.description"] = _stringToMarkdown,
+ ["ElementDefinition.mapping.comment"] = _stringToMarkdown,
+ ["CapabilityStatement.implementation.description"] = _stringToMarkdown,
+ };
+ // ReSharper restore ArrangeObjectCreationWhenTypeNotEvident
private readonly Dictionary _sinceAttributes = new()
{
@@ -388,22 +441,46 @@ private record SinceVersion(FhirReleases.FhirSequenceCodes Since);
["Signature.contentType"] = ("R4", "")
};
+ private static string? GetExplicitName(ElementDefinition ed, FhirReleases.FhirSequenceCodes sequence) =>
+ (ed.Path, sequence) switch
+ {
+ ("Evidence.statistic.attributeEstimate.attributeEstimate", _) => "AttributeEstimate",
+ ("Citation.citedArtifact.contributorship.summary", _) => "CitedArtifactContributorshipSummary",
+ ("Measure.group", FhirReleases.FhirSequenceCodes.R6) => "GroupBackboneComponent",
+ ("Measure.group.component", FhirReleases.FhirSequenceCodes.R6) => "GroupComponent",
+ ("Measure.group.stratifier.component", FhirReleases.FhirSequenceCodes.R6) => "GroupStratifierComponent",
+ ("MolecularDefinition.location.sequenceLocation.coordinateInterval", FhirReleases.FhirSequenceCodes.R6) =>
+ "LocationSequenceLocationCoordinateIntervalComponent",
+ ("MolecularDefinition.location.sequenceLocation.coordinateInterval.coordinateSystem", FhirReleases.FhirSequenceCodes.R6) =>
+ "LocationSequenceLocationCoordinateIntervalCoordinateSystemComponent",
+ ("MolecularDefinition.representation.extracted.coordinateInterval", FhirReleases.FhirSequenceCodes.R6) =>
+ "RepresentationExtractedCoordinateIntervalComponent",
+ ("MolecularDefinition.representation.extracted.coordinateInterval.coordinateSystem", FhirReleases.FhirSequenceCodes.R6) =>
+ "RepresentationExtractedCoordinateIntervalCoordinateSystemComponent",
+ ("MolecularDefinition.representation.relative.edit.coordinateInterval", FhirReleases.FhirSequenceCodes.R6) =>
+ "RepresentationRelativeEditCoordinateIntervalComponent",
+ ("MolecularDefinition.representation.relative.edit.coordinateInterval.coordinateSystem", FhirReleases.FhirSequenceCodes.R6) =>
+ "RepresentationRelativeEditCoordinateIntervalCoordinateSystemComponent",
+
+ ("TestPlan.scope", FhirReleases.FhirSequenceCodes.R6) => "ScopeComponent",
+ ("TestPlan.testCase.scope", FhirReleases.FhirSequenceCodes.R6) => "TestCaseScopeComponent",
+ _ => ed.cgExplicitName()
+ };
+
/// True to export five ws.
private bool _exportFiveWs = true;
/// Gets the FHIR primitive type map.
/// The FHIR primitive type map.
- Dictionary ILanguage.FhirPrimitiveTypeMap => CSharpFirelyCommon.PrimitiveTypeMap;
+ Dictionary ILanguage.FhirPrimitiveTypeMap => PrimitiveTypeMap;
/// If a Cql ModelInfo is available, this will be the parsed XML model file.
private Ncqa.Cql.Model.ModelInfo? _cqlModelInfo = null;
private IDictionary? _cqlModelClassInfo = null;
/// Export the passed FHIR version into the specified directory.
+ ///
/// The information.
- /// Information describing the server.
- /// Options for controlling the operation.
- /// Directory to write files.
public void Export(object untypedOptions, DefinitionCollection info)
{
if (untypedOptions is not FirelyGenOptions options)
@@ -415,24 +492,31 @@ public void Export(object untypedOptions, DefinitionCollection info)
// STU3 satellite is a combination of satellite and conformance
if ((info.FhirSequence == FhirReleases.FhirSequenceCodes.STU3) &&
- (subset == CSharpFirelyCommon.GenSubset.Satellite))
+ (subset == GenSubset.Satellite))
{
- subset = CSharpFirelyCommon.GenSubset.Satellite | CSharpFirelyCommon.GenSubset.Conformance;
+ subset = GenSubset.Satellite | GenSubset.Conformance;
}
+ // By definition, we should not have any element type changes for sattelites, they
+ // should only have their own, defined types from the spec.
+ if (subset.HasFlag(GenSubset.Satellite))
+ _elementTypeChanges.Clear();
+
// only generate base definitions for R5
- if (subset.HasFlag(GenSubset.Base) && info.FhirSequence != FhirReleases.FhirSequenceCodes.R5)
+ if (subset.HasFlag(GenSubset.Base) &&
+ info.FhirSequence is not (FhirReleases.FhirSequenceCodes.R6 or FhirReleases.FhirSequenceCodes.R5))
{
- Console.WriteLine($"Aborting {LanguageName} for {info.FhirSequence}: code generation for the 'base' subset should be run on R5 only.");
+ Console.WriteLine($"Aborting {LanguageName} for {info.FhirSequence}: code generation for the 'base' subset should be run on R5/R6 only.");
return;
}
// conformance subset is only valid for STU3 and R5
if (subset.HasFlag(GenSubset.Conformance) &&
- (info.FhirSequence != FhirReleases.FhirSequenceCodes.STU3 &&
- info.FhirSequence != FhirReleases.FhirSequenceCodes.R5))
+ info.FhirSequence is not (FhirReleases.FhirSequenceCodes.STU3 or
+ FhirReleases.FhirSequenceCodes.R5 or FhirReleases.FhirSequenceCodes.R6))
+
{
- Console.WriteLine($"Aborting {LanguageName} for {info.FhirSequence}: code generation for the 'conformance' subset should be run on STU3 or R5 only.");
+ Console.WriteLine($"Aborting {LanguageName} for {info.FhirSequence}: code generation for the 'conformance' subset should be run on STU3 or R5/R6 only.");
return;
}
@@ -457,8 +541,8 @@ public void Export(object untypedOptions, DefinitionCollection info)
string cqlModelResourceKey = options.CqlModel;
if (!string.IsNullOrEmpty(cqlModelResourceKey))
{
- _cqlModelInfo = Ncqa.Cql.Model.CqlModels.LoadEmbeddedResource(cqlModelResourceKey);
- _cqlModelClassInfo = Ncqa.Cql.Model.CqlModels.ClassesByName(_cqlModelInfo);
+ _cqlModelInfo = CqlModels.LoadEmbeddedResource(cqlModelResourceKey);
+ _cqlModelClassInfo = CqlModels.ClassesByName(_cqlModelInfo);
}
var allPrimitives = new Dictionary();
@@ -485,36 +569,36 @@ public void Export(object untypedOptions, DefinitionCollection info)
WriteGenerationComment(infoWriter);
- if (options.ExportStructures.Contains(CodeGenCommon.Models.FhirArtifactClassEnum.ValueSet))
+ if (options.ExportStructures.Contains(FhirArtifactClassEnum.ValueSet))
{
WriteSharedValueSets(subset);
}
_modelWriter.WriteLineIndented("// Generated items");
- if (options.ExportStructures.Contains(CodeGenCommon.Models.FhirArtifactClassEnum.PrimitiveType))
+ if (options.ExportStructures.Contains(FhirArtifactClassEnum.PrimitiveType))
{
WritePrimitiveTypes(_info.PrimitiveTypesByName.Values, ref dummy, subset);
}
AddModels(allPrimitives, _info.PrimitiveTypesByName.Values);
- if (options.ExportStructures.Contains(CodeGenCommon.Models.FhirArtifactClassEnum.ComplexType))
+ if (options.ExportStructures.Contains(FhirArtifactClassEnum.ComplexType))
{
WriteComplexDataTypes(_info.ComplexTypesByName.Values, ref dummy, subset);
}
AddModels(allComplexTypes, _info.ComplexTypesByName.Values);
- AddModels(allComplexTypes, _sharedR5DataTypes);
+ AddModels(allComplexTypes, SharedR5DataTypes);
- if (options.ExportStructures.Contains(CodeGenCommon.Models.FhirArtifactClassEnum.Resource))
+ if (options.ExportStructures.Contains(FhirArtifactClassEnum.Resource))
{
WriteResources(_info.ResourcesByName.Values, ref dummy, subset);
}
AddModels(allResources, _info.ResourcesByName.Values);
- if (options.ExportStructures.Contains(CodeGenCommon.Models.FhirArtifactClassEnum.Interface))
+ if (options.ExportStructures.Contains(FhirArtifactClassEnum.Interface))
{
WriteInterfaces(_info.InterfacesByName.Values, ref dummy, subset);
}
@@ -559,16 +643,10 @@ private void ModifyDefinitionsForConsistency()
// update the copied element to be the content element
edContent.ElementId = "Binary.content";
edContent.Path = "Binary.content";
- edContent.Base = new() { Path = "Binary.content", Min = 0, Max = "1" };
+ edContent.Base = new ElementDefinition.BaseComponent { Path = "Binary.content", Min = 0, Max = "1" };
edContent.cgSetFieldOrder(edData.cgFieldOrder(), edData.cgComponentFieldOrder());
- //edContent.RemoveExtension(CommonDefinitions.ExtUrlEdFieldOrder);
- //edContent.RemoveExtension(CommonDefinitions.ExtUrlEdComponentFieldOrder);
-
- //edContent.AddExtension(CommonDefinitions.ExtUrlEdFieldOrder, new Integer(edData.cgFieldOrder()));
- //edContent.AddExtension(CommonDefinitions.ExtUrlEdComponentFieldOrder, new Integer(edData.cgComponentFieldOrder()));
-
// add our element and track info, note that we are not increasing
// the orders since they are duplicate elements from different versions
_ = _info.TryInsertElement(sdBinary, edContent, false);
@@ -641,12 +719,6 @@ private void ModifyDefinitionsForConsistency()
edContentType.cgSetFieldOrder(7, 6);
- //edContentType.RemoveExtension(CommonDefinitions.ExtUrlEdFieldOrder);
- //edContentType.RemoveExtension(CommonDefinitions.ExtUrlEdComponentFieldOrder);
-
- //edContentType.AddExtension(CommonDefinitions.ExtUrlEdFieldOrder, new Integer(6), true);
- //edContentType.AddExtension(CommonDefinitions.ExtUrlEdComponentFieldOrder, new Integer(6), true);
-
// add our element and track info
_ = _info.TryInsertElement(sdSignature, edContentType, false);
}
@@ -675,13 +747,8 @@ private void ModifyDefinitionsForConsistency()
edOnBehalfOf.Base.Path = "Signature.onBehalfOf[x]";
edOnBehalfOf.Type.Add(new() { Code = "uri" });
- int prevFO = edOnBehalfOf.cgFieldOrder();
- int prevCFO = edOnBehalfOf.cgComponentFieldOrder();
-
// TODO: fix the order (should be 6th total, 5th in component)
edOnBehalfOf.cgSetFieldOrder(6, 5);
-
- //_ = _info.TryUpdateElement(sdSignature, edOnBehalfOf, prevFO, prevCFO);
}
}
@@ -719,12 +786,6 @@ private void ModifyDefinitionsForConsistency()
edFocus.cgSetFieldOrder(123, 3);
- //edFocus.RemoveExtension(CommonDefinitions.ExtUrlEdFieldOrder);
- //edFocus.RemoveExtension(CommonDefinitions.ExtUrlEdComponentFieldOrder);
-
- //edFocus.AddExtension(CommonDefinitions.ExtUrlEdFieldOrder, new Integer(123), true);
- //edFocus.AddExtension(CommonDefinitions.ExtUrlEdComponentFieldOrder, new Integer(3), true);
-
// TODO(ginoc): This insertion is currently pushing exclusionCriteria to Order=60 in file (componentOrder 5) - it should not
// add our element and track info
_ = _info.TryInsertElement(sdValueSet, edFocus, true);
@@ -770,12 +831,6 @@ private void ModifyDefinitionsForConsistency()
edXPath.cgSetFieldOrder(66, 8);
}
- //edXPath.RemoveExtension(CommonDefinitions.ExtUrlEdFieldOrder);
- //edXPath.RemoveExtension(CommonDefinitions.ExtUrlEdComponentFieldOrder);
-
- //edXPath.AddExtension(CommonDefinitions.ExtUrlEdFieldOrder, new Integer(65), true);
- //edXPath.AddExtension(CommonDefinitions.ExtUrlEdComponentFieldOrder, new Integer(7), true);
-
// add our element and track info
_ = _info.TryInsertElement(sdElementDefinition, edXPath, true);
}
@@ -806,20 +861,6 @@ private void ModifyDefinitionsForConsistency()
_ = _info.TryInsertElement(sdRelatedArtifact, edUrl, true);
}
- // need to modify the summary status of narrative elements - remove this for SDK 6.0
- if (_info.ComplexTypesByName.TryGetValue("Narrative", out StructureDefinition? sdNarrative))
- {
- if (sdNarrative.cgTryGetElementById("Narrative.status", out ElementDefinition? edStatus))
- {
- edStatus.IsSummary = true;
- }
-
- if (sdNarrative.cgTryGetElementById("Narrative.div", out ElementDefinition? edDiv))
- {
- edDiv.IsSummary = true;
- }
- }
-
// correct issues in FHIR 6.0.0-ballot2
if ((_info.MainPackageId == "hl7.fhir.r6.core") && (_info.MainPackageVersion == "6.0.0-ballot2"))
{
@@ -870,6 +911,7 @@ private void WriteModelInfo(
WriteGenerationComment();
_writer.WriteLineIndented("using System;");
+ _writer.WriteLineIndented("using System.Collections;");
_writer.WriteLineIndented("using System.Collections.Generic;");
_writer.WriteLineIndented("using Hl7.Fhir.Introspection;");
_writer.WriteLineIndented("using Hl7.Fhir.Validation;");
@@ -890,14 +932,16 @@ private void WriteModelInfo(
// open class
OpenScope();
- WriteSupportedResources(writtenResources.Values.Where(SupportedResourcesFilter));
+ WriteSupportedResources(writtenResources.Values.Where(_supportedResourcesFilter));
WriteFhirVersion();
- WriteFhirToCs(writtenPrimitives.Values.Where(FhirToCsFilter), writtenComplexTypes.Values.Where(FhirToCsFilter), writtenResources.Values.Where(FhirToCsFilter));
- WriteCsToString(writtenPrimitives.Values.Where(CsToStringFilter), writtenComplexTypes.Values.Where(CsToStringFilter), writtenResources.Values.Where(CsToStringFilter));
+ WriteFhirToCs(writtenPrimitives.Values.Where(_fhirToCsFilter), writtenComplexTypes.Values.Where(_fhirToCsFilter), writtenResources.Values.Where(_fhirToCsFilter));
+ WriteCsToString(writtenPrimitives.Values.Where(_csToStringFilter), writtenComplexTypes.Values.Where(_csToStringFilter), writtenResources.Values.Where(_csToStringFilter));
WriteSearchParameters();
+ var dataTypes = writtenPrimitives.Concat(writtenComplexTypes).ToDictionary(we => we.Key, we => we.Value);
+ WriteOpenTypes(dataTypes);
// close class
CloseScope();
@@ -921,6 +965,30 @@ private void WriteModelInfo(
}
}
+
+ private void WriteOpenTypes(Dictionary types)
+ {
+ _writer.WriteIndentedComment("The open types that are used in the FHIR model. These are types that can be used in the 'value' field of an Extension.", isSummary: true);
+ _writer.WriteIndentedComment("This list differs from the one in the written documentation (https://www.hl7.org/fhir/datatypes.html),\n" +
+ "but we assume the list in Extension.value[x] is the more authorative.",
+ isRemarks: true, isSummary: false);
+ _writer.WriteLineIndented("public static readonly Type[] OpenTypes =");
+ OpenScope();
+
+ var extensionValue = _info.TryFindElementByPath("Extension.value[x]",
+ out StructureDefinition? _,
+ out ElementDefinition? edExtensionValue)
+ ? edExtensionValue.Type
+ : throw new InvalidOperationException("Could not find Extension.value[x] element in definitions");
+
+ foreach (var typeName in extensionValue.Select(ev => ev.Code).OrderBy(c => c))
+ {
+ _writer.WriteLineIndented($"typeof({types[typeName].CsName}),");
+ }
+
+ CloseScope(includeSemicolon: true);
+ }
+
/// Writes the search parameters.
private void WriteSearchParameters()
{
@@ -937,6 +1005,13 @@ private void WriteSearchParameters()
foreach (SearchParameter sp in resourceSearchParams.Values.OrderBy(s => s.Name))
{
+ // TODO:R6: Add support for the 'resource' search parameter type
+ if ((sp.TypeElement.ObjectValue is "resource"))
+ {
+ Console.WriteLine($"Skipping SearchParameter {sp.Id} ({sp.Url}) because it is target type of 'resource'!!!");
+ continue;
+ }
+
if (sp.Experimental == true)
{
continue;
@@ -971,7 +1046,7 @@ private void WriteSearchParameters()
.Split(_splitChars, StringSplitOptions.RemoveEmptyEntries)
.Where(s => s.StartsWith(complex.Name + ".", StringComparison.Ordinal));
- path = "\"" + string.Join("\", \"", split) + "\", ";
+ path = "\"" + string.Join("\", \"", split) + "\"";
}
string target;
@@ -982,12 +1057,8 @@ private void WriteSearchParameters()
}
else
{
- SortedSet sc = [];
-
- foreach (string t in sp.Target.Select(t => t.GetLiteral()!))
- {
- sc.Add("ResourceType." + t);
- }
+ List sc =
+ [..sp.Target.Where(t => t != null).Cast()];
// HACK: for http://hl7.org/fhir/SearchParameter/clinical-encounter,
// none of the base resources have EpisodeOfCare as target, except
@@ -1003,19 +1074,29 @@ private void WriteSearchParameters()
{
if (complex.Name != "Procedure" && complex.Name != "DeviceRequest")
{
- sc.Remove("ResourceType.EpisodeOfCare");
+ sc.Remove(VersionIndependentResourceTypesAll.EpisodeOfCare);
}
}
else
{
if (complex.Name != "DocumentReference")
{
- sc.Remove("ResourceType.EpisodeOfCare");
+ sc.Remove(VersionIndependentResourceTypesAll.EpisodeOfCare);
}
}
}
- target = ", Target = new ResourceType[] { " + string.Join(", ", sc) + ", }";
+ if (_info.FhirSequence != FhirReleases.FhirSequenceCodes.STU3)
+ {
+ if(_searchParamDefsTargetRemovals.TryGetValue((complex.Name,sp.Name), out var targetRemoval))
+ {
+ sc.Remove(targetRemoval);
+ }
+ }
+
+ var targetStrings = sc.Select(s => $"VersionIndependentResourceTypesAll.{s.GetLiteral()}")
+ .Order();
+ target = $", Target = [{string.Join(", ", targetStrings)}]";
}
string xpath = string.IsNullOrEmpty(sp.cgXPath()) ? string.Empty : ", XPath = \"" + sp.cgXPath() + "\"";
@@ -1035,7 +1116,7 @@ private void WriteSearchParameters()
$" Description = @\"{SanitizeForMarkdown(description)}\"," :
$" Description = new Markdown(@\"{SanitizeForMarkdown(description)}\"),") +
$" Type = SearchParamType.{searchType}," +
- $" Path = new string[] {{ {path}}}" +
+ $" Path = [{path}]" +
target +
xpath +
expression +
@@ -1117,10 +1198,8 @@ private void WriteFhirToCs(
/// Writes the FHIR version.
private void WriteFhirVersion()
{
- _writer.WriteLineIndented("public static string Version");
- OpenScope();
- _writer.WriteLineIndented($"get {{ return \"{_info.FhirVersionLiteral}\"; }}");
- CloseScope();
+ _writer.WriteLineIndented($"""public static string Version => "{_info.FhirVersionLiteral}";""");
+ _writer.WriteLine();
}
/// Writes the supported resources dictionary.
@@ -1163,8 +1242,7 @@ private void WriteSharedValueSets(GenSubset subset)
// traverse all versions of all value sets
foreach ((string unversionedUrl, string[] versions) in _info.ValueSetVersions.OrderBy(kvp => kvp.Key))
{
- if (_exclusionSet.Contains(unversionedUrl) ||
- (_valueSetBehaviorOverrides.TryGetValue(unversionedUrl, out ValueSetBehaviorOverrides? oddInfo) && (oddInfo.AllowShared == false)))
+ if (ExclusionSet.Contains(unversionedUrl))
{
continue;
}
@@ -1177,13 +1255,7 @@ private void WriteSharedValueSets(GenSubset subset)
continue;
}
- // we never want to write limited expansions
- //if (vs.IsLimitedExpansion())
- //{
- // continue;
- //}
-
- IEnumerable coreBindings = _info.CoreBindingsForVs(vs.Url);
+ IEnumerable coreBindings = _info.CoreBindingsForVs(vs.Url).ToList();
Hl7.Fhir.Model.BindingStrength? strongestBinding = _info.StrongestBinding(coreBindings);
if (strongestBinding != Hl7.Fhir.Model.BindingStrength.Required)
@@ -1198,7 +1270,7 @@ required bindings anywhere in the data model. */
IEnumerable referencedBy = coreBindings.cgExtractBaseTypes(_info);
- if ((referencedBy.Count() < 2) && !_explicitSharedValueSets.Contains((_info.FhirSequence.ToString(), vs.Url)))
+ if ((referencedBy.Count() < 2) && !ExplicitSharedValueSets.Contains((_info.FhirSequence.ToString(), vs.Url)))
{
/* ValueSets that are used in a single POCO are generated as a nested enum inside that
* POCO, not here in the shared valuesets */
@@ -1294,16 +1366,6 @@ private void WriteInterface(
GenSubset subset)
{
string exportName = "I" + complex.Name.ToPascalCase();
-
- //writtenModels.Add(
- // complex.Name,
- // new WrittenModelInfo()
- // {
- // FhirName = complex.Name,
- // CsName = $"{Namespace}.{exportName}",
- // IsAbstract = complex.Abstract == true,
- // });
-
string filename = Path.Combine(_exportDirectory, "Generated", $"{exportName}.cs");
_modelWriter.WriteLineIndented($"// {exportName}.cs");
@@ -1359,7 +1421,7 @@ private void WriteResources(
{
foreach (StructureDefinition complex in complexes.OrderBy(c => c.Name))
{
- if (_exclusionSet.Contains(complex.Name))
+ if (ExclusionSet.Contains(complex.Name))
{
continue;
}
@@ -1386,12 +1448,8 @@ private void WriteResource(
writtenModels.Add(
complex.Name,
- new WrittenModelInfo()
- {
- FhirName = complex.Name,
- CsName = $"{Namespace}.{exportName}",
- IsAbstract = complex.Abstract == true,
- });
+ new WrittenModelInfo(FhirName: complex.Name, CsName: $"{Namespace}.{exportName}",
+ IsAbstract: complex.Abstract == true));
string filename = Path.Combine(_exportDirectory, "Generated", $"{exportName}.cs");
@@ -1447,7 +1505,7 @@ private void WriteComplexDataTypes(
{
foreach (StructureDefinition complex in complexes.OrderBy(c => c.Name))
{
- if (_exclusionSet.Contains(complex.Name))
+ if (ExclusionSet.Contains(complex.Name))
{
continue;
}
@@ -1479,12 +1537,8 @@ private void WriteComplexDataType(
writtenModels.Add(
complex.Name,
- new WrittenModelInfo()
- {
- FhirName = complex.Name,
- CsName = $"{Namespace}.{exportName}",
- IsAbstract = complex.Abstract == true,
- });
+ new WrittenModelInfo(FhirName: complex.Name, CsName: $"{Namespace}.{exportName}",
+ IsAbstract: complex.Abstract == true));
string filename = Path.Combine(_exportDirectory, "Generated", $"{exportName}.cs");
@@ -1541,12 +1595,6 @@ private void WriteInterfaceComponent(
WriteIndentedComment($"{complex.Element.Short}");
- //WriteSerializable();
-
- string fhirTypeConstructor = $"\"{complexName}\",\"{complex.cgUrl()}\"";
-
- //_writer.WriteLineIndented($"[FhirType({fhirTypeConstructor}, IsResource=true)]");
-
StructureDefinition? parentInterface = _info.GetParentInterface(complex.Structure);
if (parentInterface == null)
@@ -1567,29 +1615,6 @@ private void WriteInterfaceComponent(
// open class
OpenScope();
- // right now, no interfaces have components - TODO: determine naming convention if this comes up
- //foreach (ComponentDefinition component in complex.cgChildComponents(_info))
- //{
- // string componentExportName;
- // if (string.IsNullOrEmpty(component.cgExplicitName()))
- // {
- // componentExportName =
- // $"{component.cgName(NamingConvention.PascalCase)}Component";
- // }
- // else
- // {
- // // Consent.provisionActorComponent is explicit lower case...
- // componentExportName =
- // $"{component.cgExplicitName()}" +
- // $"Component";
- // }
- // WriteBackboneComponent(
- // component,
- // componentExportName,
- // exportName,
- // subset);
- //}
-
WriteInterfaceElements(complex, exportName, ref exportedElements);
// close class
@@ -1623,7 +1648,7 @@ private void WriteInterfaceComponent(
string pn = exportName + "." + interfaceEi.PropertyName;
WrittenElementInfo? resourceEi = null;
- if (resourceElements.TryGetValue(interfaceEi.FhirElementName ?? string.Empty, out ElementDefinition? resourceEd))
+ if (resourceElements.TryGetValue(interfaceEi.FhirElementName, out ElementDefinition? resourceEd))
{
resourceEi = BuildElementInfo(resourceExportName, resourceEd);
}
@@ -1648,65 +1673,28 @@ private void WriteInterfaceElementGettersAndSetters(
string interfaceExportName,
WrittenElementInfo interfaceEi)
{
- string pn = interfaceExportName + "." + interfaceEi.PropertyName;
- string rt = resourceEi?.PropertyType.PropertyTypeString ?? string.Empty;
- string it = interfaceEi.PropertyType.PropertyTypeString;
+ string pn = interfaceEi.PropertyName;
+ bool isList = interfaceEi.PropertyType is ListTypeReference;
+ string it = isList ? interfaceEi.PropertyType.PropertyTypeString : WithNullabilityMarking(interfaceEi.PropertyType.PropertyTypeString);
if ((resourceEd == null) || (resourceEi == null))
{
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented($"{it} {pn}");
- OpenScope();
- _writer.WriteLineIndented($"get {{ return null; }}");
- _writer.WriteLineIndented($"set {{ throw new NotImplementedException(\"Resource {resourceExportName} does not implement {interfaceExportName}.{interfaceEi.FhirElementName}\");}}");
- CloseScope();
+ writeEmptyGetterAndSetter(it, pn, isList);
}
else if (interfaceEi.PropertyType.PropertyTypeString == resourceEi.PropertyType.PropertyTypeString)
{
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented($"{it} {pn}" +
- $" {{" +
- $" get => {resourceEi.PropertyName};" +
- $" set {{ {resourceEi.PropertyName} = value; }}" +
- $" }}");
- _writer.WriteLine();
+ writeOneOnOneGetterAndSetter(it, pn, isList);
}
+
// a resource is allowed to have a scalar in place of a list
- else if ((interfaceEi.PropertyType is ListTypeReference interfaceLTR) &&
- (interfaceLTR.Element.PropertyTypeString == resourceEi.PropertyType.PropertyTypeString))
+ else if ((interfaceEi.PropertyType is ListTypeReference interfaceLtr) &&
+ (interfaceLtr.Element.PropertyTypeString == resourceEi.PropertyType.PropertyTypeString))
{
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented($"{it} {pn}");
- OpenScope();
- //_writer.WriteLineIndented($"get {{ return new {it}() {{ {resourceEi.PropertyName} }}; }}");
- _writer.WriteLineIndented("get");
- OpenScope(); // getter
- _writer.WriteLineIndented($"if ({resourceEi.PropertyName} == null) return new {it}();");
- _writer.WriteLineIndented($"return new {it}() {{ {resourceEi.PropertyName} }};");
- CloseScope(); // getter
-
- _writer.WriteLineIndented("set");
- OpenScope();
- _writer.WriteLineIndented($"if (value.Count == 0) {{ {resourceEi.PropertyName} = null; }}");
- _writer.WriteLineIndented($"else if (value.Count == 1) {{ {resourceEi.PropertyName} = value.First(); }}");
- _writer.WriteLineIndented($"else {{ throw new NotImplementedException(\"Resource {resourceExportName} can only have a single {pn} value\"); }}");
- CloseScope();
-
- CloseScope();
+ writeSingleToListGetterAndSetter(it, pn, isList);
}
else
{
- WriteIndentedComment(
- $"{resourceExportName}.{resourceEi.PropertyName} ({resourceEi.PropertyType}) is incompatible with\n" +
- $"{interfaceExportName}.{interfaceEi.FhirElementName} ({interfaceEi.PropertyType})",
- isSummary: false,
- isRemarks: true);
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented($"{it} {pn}");
- OpenScope();
- _writer.WriteLineIndented($"get {{ return null; }}");
- _writer.WriteLineIndented($"set {{ throw new NotImplementedException(\"{resourceExportName}.{resourceEi.PropertyName} ({resourceEi.PropertyType}) is incompatible with {interfaceExportName}.{interfaceEi.FhirElementName} ({interfaceEi.PropertyType})\");}}");
- CloseScope();
+ writeIncompatibleGetterSetter(it, pn, isList);
}
if (!TryGetPrimitiveType(interfaceEi.PropertyType, out PrimitiveTypeReference? interfacePtr))
@@ -1714,69 +1702,105 @@ private void WriteInterfaceElementGettersAndSetters(
return;
}
- string ppn = interfaceExportName + "." + interfaceEi.PrimitiveHelperName;
- string prt = (resourceEi?.PropertyType is PrimitiveTypeReference rPTR) ? rPTR.ConveniencePropertyTypeString : string.Empty;
- string pit = interfacePtr.ConveniencePropertyTypeString;
+ string ppn = interfaceEi.PrimitiveHelperName!;
+ string pit = isList ? interfacePtr.ConveniencePropertyTypeString : WithNullabilityMarking(interfacePtr.ConveniencePropertyTypeString);
if ((resourceEd == null) || (resourceEi == null))
{
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented($"{pit} {ppn}");
- OpenScope();
- _writer.WriteLineIndented($"get {{ return null; }}");
- _writer.WriteLineIndented($"set {{ throw new NotImplementedException(\"Resource {resourceExportName} does not implement {interfaceExportName}.{interfaceEi.FhirElementName}\");}}");
- CloseScope();
+ writeEmptyGetterAndSetter(pit, ppn, isList);
}
else if (interfaceEi.PropertyType == resourceEi.PropertyType)
{
+ writeOneOnOneGetterAndSetter(pit, ppn, isList);
+ }
+ else
+ {
+ writeIncompatibleGetterSetter(pit, ppn, isList);
+ }
+
+ return;
+
+ void writeOneOnOneGetterAndSetter(string propertyType, string propertyName, bool allowNull)
+ {
+ if(allowNull)
+ _writer.WriteLineIndented("[AllowNull]");
_writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented($"{pit} {ppn}" +
- $" {{" +
- $" get => {resourceEi.PrimitiveHelperName};" +
- $" set {{ {resourceEi.PrimitiveHelperName} = value; }}" +
- $" }}");
- _writer.WriteLine();
+ _writer.WriteLineIndented($"{propertyType} {interfaceExportName}.{propertyName}");
+ OpenScope();
+ _writer.WriteLineIndented($"get => {propertyName};");
+ _writer.WriteLineIndented($"set => {propertyName} = value!;");
+ CloseScope();
}
- // a resource is allowed to have a scalar in place of a list
- //else if (interfaceEi.PropertyType == "List<" + resourceEi.PropertyType + ">")
- else if (interfaceEi.PropertyType is ListTypeReference)
+
+ void writeSingleToListGetterAndSetter(string propertyType, string propertyName, bool allowNull)
{
+ if (allowNull)
+ _writer.WriteLineIndented("[AllowNull]");
_writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented($"{pit} {ppn}");
+ _writer.WriteLineIndented($"{propertyType} {interfaceExportName}.{propertyName}");
OpenScope();
- _writer.WriteLineIndented($"get {{ return new {pit}() {{ {resourceEi.PropertyType.PropertyTypeString} }}; }}");
+
+ _writer.WriteLineIndented($"get => {propertyName} is null ? [] : [{propertyName}];");
_writer.WriteLineIndented("set");
OpenScope();
- _writer.WriteLineIndented($"if (value.Count == 1) {{ {resourceEi.PrimitiveHelperName} = value.First(); }}");
- _writer.WriteLineIndented($"else {{ throw new NotImplementedException(\"Resource {resourceExportName} can only have a single {ppn} value\"); }}");
+ _writer.WriteLineIndented($"{propertyName} = value switch");
+
+ OpenScope();
+ _writer.WriteLineIndented($"{{ Count: 0 }} => null,");
+ _writer.WriteLineIndented($"{{ Count: 1 }} => value.First(),");
+ _writer.WriteLineIndented($"_ => throw new NotImplementedException(\"Resource {resourceExportName} can only have a single {propertyName} value\")");
+ CloseScope(includeSemicolon: true, suppressNewline: true);
+
+ CloseScope(suppressNewline: true);
CloseScope();
+ }
+
+ void writeIncompatibleGetterSetter(string propertyType, string propertyName, bool allowNull)
+ {
+ string message = $"{resourceExportName}.{resourceEi.PropertyName} is incompatible with " +
+ $"{interfaceExportName}.{interfaceEi.FhirElementName}.";
+ WriteIndentedComment(message, isSummary: false, isRemarks: true);
+
+ if (allowNull)
+ _writer.WriteLineIndented("[AllowNull]");
+ _writer.WriteLineIndented("[IgnoreDataMember]");
+ _writer.WriteLineIndented($"{propertyType} {interfaceExportName}.{propertyName}");
+ OpenScope();
+ _writer.WriteLineIndented($"get => {emptyInterfaceType()};");
+ _writer.WriteLineIndented($"set => throw new NotImplementedException(\"{message}\");");
CloseScope();
}
- else
+
+ string emptyInterfaceType() => interfaceEi.PropertyType is ListTypeReference ? "[]" : "null";
+
+ void writeEmptyGetterAndSetter(string propertyType, string propertyName, bool allowNull)
{
- _writer.WriteLineIndented($"// {resourceExportName}.{resourceEi.PropertyName} ({prt}) is incompatible with {interfaceExportName}.{interfaceEi.FhirElementName} ({pit})");
+ if (allowNull)
+ _writer.WriteLineIndented("[AllowNull]");
_writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented($" {pit} {ppn}");
+ _writer.WriteLineIndented($"{propertyType} {interfaceExportName}.{propertyName}");
OpenScope();
- _writer.WriteLineIndented($"get {{ return null; }}");
- _writer.WriteLineIndented($"set {{ throw new NotImplementedException(\"{resourceExportName}.{resourceEi.PropertyName} ({resourceEi.PropertyType.PropertyTypeString}) is incompatible with {interfaceExportName}.{interfaceEi.FhirElementName} ({interfaceEi.PropertyType.PropertyTypeString})\");}}");
+ _writer.WriteLineIndented($"get => {emptyInterfaceType()};");
+ _writer.WriteLineIndented($"set => throw new NotImplementedException(\"Resource {resourceExportName}" +
+ $" does not implement {interfaceExportName}.{interfaceEi.FhirElementName}\");");
CloseScope();
}
}
+ private static string WithNullabilityMarking(string type) => type.EndsWith("?") ? type : type + "?";
+
+
private void WriteInterfaceElements(
ComponentDefinition complex,
string exportedComplexName,
ref List exportedElements)
{
- var elementsToGenerate = complex.cgGetChildren()
+ IOrderedEnumerable elementsToGenerate = complex.cgGetChildren()
.Where(e => !e.cgIsInherited(complex.Structure))
.OrderBy(e => e.cgFieldOrder());
- int orderOffset = complex.Element.cgFieldOrder();
-
string structureName = complex.cgName();
foreach (ElementDefinition element in elementsToGenerate)
@@ -1785,29 +1809,26 @@ private void WriteInterfaceElements(
exportedElements.Add(ei);
string name = element.cgName(removeChoiceMarker: true);
- var since = _sinceAttributes.TryGetValue(element.Path, out string? s) ? s : null;
- var until = _untilAttributes.TryGetValue(element.Path, out (string, string) u) ? u : default((string, string)?);
+ string? since = _sinceAttributes.GetValueOrDefault(element.Path);
+ (string, string)? until = _untilAttributes.TryGetValue(element.Path, out (string, string) u) ? u : default((string, string)?);
- var description = AttributeDescriptionWithSinceInfo(name, element.Short.Replace("{{title}}", structureName), since, until);
+ string? description = MakeAttributeRemarkForNewOrDeprecatedProperties(name, element.Short.Replace("{{title}}", structureName), since, until);
- if (TryGetPrimitiveType(ei.PropertyType, out PrimitiveTypeReference? eiPTR))
+ if (TryGetPrimitiveType(ei.PropertyType, out PrimitiveTypeReference? eiPtr))
{
WriteIndentedComment(element.Short.Replace("{{title}}", structureName));
_writer.WriteLineIndented($"/// This uses the native .NET datatype, rather than the FHIR equivalent");
- _writer.WriteLineIndented($"{eiPTR.ConveniencePropertyTypeString} {ei.PrimitiveHelperName} {{ get; set; }}");
+
+ _writer.WriteLineIndented($"{WithNullabilityMarking(eiPtr.ConveniencePropertyTypeString)} {ei.PrimitiveHelperName} {{ get; set; }}");
_writer.WriteLine();
}
- //if (ei.IsPrimitive)
- //{
- // WriteIndentedComment(element.Short.Replace("{{title}}", structureName));
- // _writer.WriteLineIndented($"/// This uses the native .NET datatype, rather than the FHIR equivalent");
- // _writer.WriteLineIndented($"{ei.PrimitiveHelperType?.Replace("Hl7.Fhir.Model.", string.Empty) ?? string.Empty} {ei.PrimitiveHelperName} {{ get; set; }}");
- // _writer.WriteLine();
- //}
-
if (description != null) WriteIndentedComment(description);
- _writer.WriteLineIndented($"{ei.PropertyType.PropertyTypeString ?? string.Empty} {ei.PropertyName} {{ get; set; }}");
+ var typ = ei.PropertyType is ListTypeReference
+ ? ei.PropertyType.PropertyTypeString
+ : WithNullabilityMarking(ei.PropertyType.PropertyTypeString);
+ _writer.WriteLineIndented("[AllowNull]");
+ _writer.WriteLineIndented($"{typ} {ei.PropertyName} {{ get; set; }}");
_writer.WriteLine();
}
}
@@ -1878,15 +1899,7 @@ private void WriteComponent(
WriteSerializable();
string fhirTypeConstructor = $"\"{complexName}\",\"{complex.cgUrl()}\"";
-
- if (isResource)
- {
- _writer.WriteLineIndented($"[FhirType({fhirTypeConstructor}, IsResource=true)]");
- }
- else
- {
- _writer.WriteLineIndented($"[FhirType({fhirTypeConstructor})]");
- }
+ _writer.WriteLineIndented($"[FhirType({fhirTypeConstructor})]");
var isPatientClass = false;
@@ -1919,18 +1932,19 @@ private void WriteComponent(
if (identifierElement.cgIsArray())
interfaces.Add("IIdentifiable>");
else
- interfaces.Add("IIdentifiable");
+ interfaces.Add("IIdentifiable");
}
}
- var primaryCodeElementInfo = isResource ? getPrimaryCodedElementInfo(complex, exportName) : null;
+ WrittenElementInfo? primaryCodeElementInfo = isResource ? getPrimaryCodedElementInfo(complex, exportName) : null;
if (primaryCodeElementInfo != null)
{
- interfaces.Add($"ICoded<{primaryCodeElementInfo.PropertyType.PropertyTypeString}>");
+ string nullable = primaryCodeElementInfo.PropertyType is ListTypeReference ? "" : "?";
+ interfaces.Add($"ICoded<{primaryCodeElementInfo.PropertyType.PropertyTypeString}{nullable}>");
}
- var modifierElement = complex.cgGetChild("modifierExtension");
+ ElementDefinition? modifierElement = complex.cgGetChild("modifierExtension");
if (modifierElement != null)
{
if (!modifierElement.cgIsInherited(complex.Structure))
@@ -1954,7 +1968,8 @@ private void WriteComponent(
// open class
OpenScope();
- WritePropertyTypeName(complex.cgName());
+ if(complex.Structure.Abstract != true)
+ WritePropertyTypeName(complex.cgName());
string validationRegEx = complex.cgValidationRegEx();
if (!string.IsNullOrEmpty(validationRegEx))
@@ -1975,16 +1990,14 @@ private void WriteComponent(
{
string componentExportName;
- if (string.IsNullOrEmpty(component.cgExplicitName()))
+ if (GetExplicitName(component.Element, _info.FhirSequence) is {} explicitName)
{
- componentExportName =
- $"{component.cgName(NamingConvention.PascalCase)}Component";
+ componentExportName = $"{explicitName}Component";
}
else
{
componentExportName =
- $"{component.cgExplicitName()}" +
- $"Component";
+ $"{component.cgName(NamingConvention.PascalCase)}Component";
}
WriteBackboneComponent(
@@ -1999,17 +2012,24 @@ private void WriteComponent(
if (identifierElement != null)
{
if (identifierElement.cgIsArray())
- _writer.WriteLineIndented("List IIdentifiable>.Identifier { get => Identifier; set => Identifier = value; }");
+ _writer.WriteLineIndented("List IIdentifiable>.Identifier { get => Identifier; set => Identifier = value!; }");
else
- _writer.WriteLineIndented("Identifier IIdentifiable.Identifier { get => Identifier; set => Identifier = value; }");
+ _writer.WriteLineIndented("Identifier? IIdentifiable.Identifier { get => Identifier; set => Identifier = value!; }");
_writer.WriteLine(string.Empty);
}
if (primaryCodeElementInfo != null)
{
- _writer.WriteLineIndented($"{primaryCodeElementInfo.PropertyType.PropertyTypeString} ICoded<{primaryCodeElementInfo.PropertyType.PropertyTypeString}>.Code {{ get => {primaryCodeElementInfo.PropertyName}; set => {primaryCodeElementInfo.PropertyName} = value; }}");
- _writer.WriteLineIndented($"IEnumerable ICoded.ToCodings() => {primaryCodeElementInfo.PropertyName}.ToCodings();");
+ var (codedType, bang) = primaryCodeElementInfo.PropertyType switch
+ {
+ ListTypeReference { PropertyTypeString: { } n } => (n, string.Empty),
+ { PropertyTypeString: {} n } => (WithNullabilityMarking(n), "!")
+ };
+
+ _writer.WriteLineIndented($"{codedType} ICoded<{codedType}>.Code {{ get => {primaryCodeElementInfo.PropertyName}; " +
+ $"set => {primaryCodeElementInfo.PropertyName} = value{bang}; }}");
+ _writer.WriteLineIndented($"IReadOnlyCollection ICoded.ToCodings() => {primaryCodeElementInfo.PropertyName}?.ToCodings() ?? [];");
_writer.WriteLine(string.Empty);
}
@@ -2019,7 +2039,7 @@ private void WriteComponent(
if (birthdayProperty != null)
{
- _writer.WriteLineIndented($"Hl7.Fhir.Model.Date {Namespace}.IPatient.BirthDate => {birthdayProperty.PropertyName};");
+ _writer.WriteLineIndented($"Hl7.Fhir.Model.Date? {Namespace}.IPatient.BirthDate => {birthdayProperty.PropertyName};");
_writer.WriteLine(string.Empty);
}
}
@@ -2031,12 +2051,8 @@ private void WriteComponent(
WriteDeepCopy(exportName);
}
- WriteMatches(exportName, exportedElements);
- WriteIsExactly(exportName, exportedElements);
- WriteChildren(exportName, exportedElements);
- WriteNamedChildren(exportName, exportedElements);
-
- WriteIDictionarySupport(exportName, exportedElements);
+ WriteCompareChildren(exportName, exportedElements);
+ WriteDictionarySupport(exportName, exportedElements);
// close class
CloseScope();
@@ -2093,23 +2109,27 @@ private string DetermineExportedBaseTypeName(string baseTypeName)
return baseTypeName;
}
- private void WriteIDictionarySupport(string exportName, IEnumerable exportedElements)
+ private string getDynamicTypeForAbstractTypeName(string abstractTypeName) =>
+ abstractTypeName switch
+ {
+ "Hl7.Fhir.Model.Resource" => "DynamicResource",
+ "Hl7.Fhir.Model.DataType" => "DynamicDataType",
+ "Hl7.Fhir.Model.PrimitiveType" => "DynamicPrimitive",
+ { } s => s
+ };
+
+ private void WriteDictionarySupport(string exportName, List exportedElements)
{
WriteDictionaryTryGetValue(exportName, exportedElements);
+ WriteDictionaryTrySetValue(exportName, exportedElements);
WriteDictionaryPairs(exportName, exportedElements);
}
-
- private string NullCheck(string propertyName, bool isList) =>
- propertyName + (isList ? "?.Any() == true" : " is not null");
-
- private void WriteDictionaryPairs(string exportName, IEnumerable exportedElements)
+ private void WriteDictionaryPairs(string exportName, List exportedElements)
{
- // Base implementation differs from subclasses.
+ // Base implementation differs from subclasses and is hand-written code in a separate partical class
if (exportName == "Base")
{
- _writer.WriteLineIndented("protected virtual IEnumerable> GetElementPairs() => Enumerable.Empty>();");
- _writer.WriteLine(string.Empty);
return;
}
@@ -2118,32 +2138,27 @@ private void WriteDictionaryPairs(string exportName, IEnumerable> GetElementPairs()");
+ _writer.WriteLineIndented("public override IEnumerable> EnumerateElements()");
OpenScope();
- _writer.WriteLineIndented("foreach (var kvp in base.GetElementPairs()) yield return kvp;");
+ _writer.WriteLineIndented("foreach (var kvp in base.EnumerateElements()) yield return kvp;");
foreach (WrittenElementInfo info in exportedElements)
{
string elementProp = $"\"{info.FhirElementName}\"";
- _writer.WriteLineIndented($"if ({NullCheck(info.PropertyName, info.PropertyType is ListTypeReference)}) yield return new " +
- $"KeyValuePair({elementProp},{info.PropertyName});");
+ _writer.WriteLineIndented($"if (_{info.PropertyName}{(info.PropertyType is ListTypeReference ? "?.Any() is true" : " is not null")} && !_{info.PropertyName}.InOverflow<{getDynamicTypeForAbstractTypeName(info.PropertyType.PropertyTypeString)}>()) yield return new " +
+ $"KeyValuePair({elementProp},_{info.PropertyName});");
}
CloseScope();
}
- private void WriteDictionaryTryGetValue(string exportName, IEnumerable exportedElements)
+ private void WriteDictionaryTryGetValue(string exportName, List exportedElements)
{
- // Base implementation differs from subclasses.
- if (exportName == "Base")
- {
- _writer.WriteLineIndented("protected virtual bool TryGetValue(string key, out object value)");
- OpenScope();
- _writer.WriteLineIndented("value = default;");
- _writer.WriteLineIndented("return false;");
- CloseScope();
- return;
- }
+ // Base implementation differs from subclasses and is hand-written code in a separate partial class
+ if (exportName == "Base")
+ {
+ return;
+ }
// Don't override anything if there are no additional elements.
if (!exportedElements.Any())
@@ -2151,7 +2166,7 @@ private void WriteDictionaryTryGetValue(string exportName, IEnumerable())");
+ _writer.OpenScope();
+ _writer.WriteLineIndented($"value = Overflow[\"{key}\"];");
+ _writer.WriteLineIndented("return true;");
+ _writer.CloseScope();
+ _writer.WriteLineIndented($"value = _{propName};");
+ _writer.WriteLineIndented($"return (value as {type.PropertyTypeString}){(type is ListTypeReference ? "?.Any() is true" : " is not null")};");
_writer.DecreaseIndent();
}
@@ -2188,26 +2210,12 @@ void writeCase(string key, string propName, bool isList)
void writeBaseTryGetValue() => _writer.WriteLineIndented("return base.TryGetValue(key, out value);");
}
- /// Writes the children of this item.
- /// Name of the exported class.
- /// The exported elements.
- private void WriteNamedChildren(string exportName,
- List exportedElements)
+
+ private void WriteDictionaryTrySetValue(string exportName, List exportedElements)
{
- // Base implementation differs from subclasses.
+ // Base implementation differs from subclasses and is hand-written code in a separate partical class
if (exportName == "Base")
{
- _writer.WriteIndentedComment("""
- Enumerate all child nodes.
- Return a sequence of child elements, components and/or properties.
- Child nodes are returned as tuples with the name and the node itself, in the order defined
- by the FHIR specification.
- First returns child nodes inherited from any base class(es), recursively.
- Finally returns child nodes defined by the current class.
- """);
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented("public virtual IEnumerable NamedChildren => Enumerable.Empty();");
- _writer.WriteLine(string.Empty);
return;
}
@@ -2217,208 +2225,106 @@ Finally returns child nodes defined by the current class.
return;
}
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented("public override IEnumerable NamedChildren");
-
+ _writer.WriteLineIndented("public override Base SetValue(string key, object? value)");
OpenScope();
- _writer.WriteLineIndented("get");
+
+ _writer.WriteLineIndented("if(value is not (null or Hl7.Fhir.Model.Base or IList)) throw new ArgumentException(\"Value must be a Base or a list of Base\", nameof(value));");
+
+ // switch
+ _writer.WriteLineIndented("switch (key)");
OpenScope();
- _writer.WriteLineIndented($"foreach (var item in base.NamedChildren) yield return item;");
foreach (WrittenElementInfo info in exportedElements)
{
- if (info.PropertyType is ListTypeReference)
- {
- _writer.WriteLineIndented(
- $"foreach (var elem in {info.PropertyName})" +
- $" {{ if (elem != null)" +
- $" yield return new ElementValue(\"{info.FhirElementName}\", elem);" +
- $" }}");
- }
- else
- {
- string yr = NamedChildrenFhirTypeWrapper(info);
+ writeSetValueCase(info.FhirElementName, null, info.PropertyType, info.PropertyName, info.Required);
+ }
- _writer.WriteLineIndented(
- $"if ({info.PropertyName} != null)" +
- $" yield return new ElementValue(\"{info.FhirElementName}\", {yr});");
- }
+ void writeSetValueCase(string fhirName, string? when, TypeReference type, string propName, bool required)
+ {
+ string overflowTypeName = getDynamicTypeForAbstractTypeName(type.PropertyTypeString);
+
+ _writer.WriteLineIndented(when is not null ? $"case \"{fhirName}\" when {when}:" : $"case \"{fhirName}\":");
+
+ _writer.IncreaseIndent();
+ _writer.WriteLineIndented($"if (value is not ({type.PropertyTypeString} or null))");
+ _writer.OpenScope();
+ _writer.WriteLineIndented($"{propName} = OverflowNull<{overflowTypeName}>.INSTANCE;");
+ _writer.WriteLineIndented($"Overflow[\"{fhirName}\"] = value;");
+ _writer.CloseScope();
+
+ // Because our list properties are never null when you get them, but can be set to null,
+ // we need a bang after the assignment here for lists, but not for other elements.
+ _writer.WriteLineIndented($"else {propName} = ({type.PropertyTypeString}?)value{(type is ListTypeReference || required ? "!" : "")};");
+ _writer.WriteLineIndented($"return this;");
+ _writer.DecreaseIndent();
}
- CloseScope(suppressNewline: true);
- CloseScope();
- }
+ _writer.WriteLineIndented("default:");
+ _writer.IncreaseIndent();
+ writeBaseTrySetValue();
- // For a limited set of exceptional elements, the Children functions return a
- // complex FHIR type wrapper.
- private static string NamedChildrenFhirTypeWrapper(WrittenElementInfo info)
- {
+ _writer.DecreaseIndent();
- return info.FhirElementPath switch
- {
- "Narrative.div" => $"new FhirString({info.PropertyName}.Value)",
- "Element.id" => $"new FhirString({info.PropertyName})",
- "Extension.url" => $"new FhirUri({info.PropertyName})",
- _ => $"{info.PropertyName}"
- };
+ // end switch
+ CloseScope(includeSemicolon: false);
+
+ CloseScope();
+
+ void writeBaseTrySetValue() => _writer.WriteLineIndented("return base.SetValue(key, value);");
}
- /// Writes the children of this item.
- /// Name of the exported class.
+ /// Writes the PairwiseEquality.
+ /// Name of the export.
/// The exported elements.
- private void WriteChildren(string exportName,
+ private void WriteCompareChildren(
+ string exportName,
List exportedElements)
{
- // Base implementation differs from subclasses.
+ // Base implementation is hand-written code in a separate partial class.
if (exportName == "Base")
- {
- _writer.WriteIndentedComment(
- """
- Enumerate all child nodes.
- Return a sequence of child elements, components and/or properties.
- Child nodes are returned in the order defined by the FHIR specification.
- First returns child nodes inherited from any base class(es), recursively.
- Finally returns child nodes defined by the current class.
- """);
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented("public virtual IEnumerable Children => Enumerable.Empty();");
- _writer.WriteLine(string.Empty);
- return;
- }
-
- // Don't override anything if there are no additional elements.
- if (!exportedElements.Any())
{
return;
}
- _writer.WriteLineIndented("[IgnoreDataMember]");
- _writer.WriteLineIndented("public override IEnumerable Children");
+ _writer.WriteLineIndented("public override bool CompareChildren(Base other, IEqualityComparer comparer)");
OpenScope();
- _writer.WriteLineIndented("get");
- OpenScope();
- _writer.WriteLineIndented($"foreach (var item in base.Children) yield return item;");
+ _writer.WriteLineIndented($"if(other is not {exportName} otherT) return false;");
+ _writer.WriteLine(string.Empty);
+
+ _writer.WriteLineIndented("if(!base.CompareChildren(otherT, comparer)) return false;");
+
+ _writer.WriteLineIndented(
+ "#pragma warning disable CS8604 // Possible null reference argument - netstd2.1 has a wrong nullable signature here");
foreach (WrittenElementInfo info in exportedElements)
{
- if (info.PropertyType is ListTypeReference)
+ if(info.PropertyType is CqlTypeReference)
{
_writer.WriteLineIndented(
- $"foreach (var elem in {info.PropertyName})" +
- $" {{ if (elem != null) yield return elem; }}");
+ $"if( _{info.PropertyName} != otherT._{info.PropertyName} )" +
+ $" return false;");
+ }
+ else if (info.PropertyType is ListTypeReference)
+ {
+ _writer.WriteLineIndented(
+ $"if(!comparer.ListEquals(_{info.PropertyName}, otherT._{info.PropertyName}))" +
+ $" return false;");
}
else
{
- string yr = NamedChildrenFhirTypeWrapper(info);
_writer.WriteLineIndented(
- $"if ({info.PropertyName} != null)" +
- $" yield return {yr};");
+ $"if(!comparer.Equals(_{info.PropertyName}, otherT._{info.PropertyName}))" +
+ $" return false;");
}
}
- CloseScope(suppressNewline: true);
- CloseScope();
- }
-
- /// Writes the matches.
- /// Name of the exported class.
- /// The exported elements.
- private void WriteMatches(
- string exportName,
- List exportedElements)
- {
- _writer.WriteLineIndented("///");
+ _writer.WriteLineIndented("#pragma warning restore CS8604 // Possible null reference argument.");
+ _writer.WriteLine(string.Empty);
- // Base implementation differs from subclasses.
- if (exportName == "Base")
+ if (exportName == "PrimitiveType")
{
- _writer.WriteLineIndented("public virtual bool Matches(IDeepComparable other) => other is Base;");
- _writer.WriteLine(string.Empty);
- return;
- }
-
- if (exportName == "PrimitiveType")
- {
- _writer.WriteLineIndented("public override bool Matches(IDeepComparable other) => IsExactly(other);");
- _writer.WriteLine(string.Empty);
- return;
- }
-
- _writer.WriteLineIndented("public override bool Matches(IDeepComparable other)");
- OpenScope();
- _writer.WriteLineIndented($"var otherT = other as {exportName};");
- _writer.WriteLineIndented("if(otherT == null) return false;");
- _writer.WriteLine(string.Empty);
- _writer.WriteLineIndented("if(!base.Matches(otherT)) return false;");
-
- foreach (WrittenElementInfo info in exportedElements)
- {
- if (info.PropertyType is CqlTypeReference)
- {
- _writer.WriteLineIndented(
- $"if( {info.PropertyName} != otherT.{info.PropertyName} )" +
- $" return false;");
- }
- else
- _writer.WriteLineIndented(
- $"if( !DeepComparable.Matches({info.PropertyName}, otherT.{info.PropertyName}))" +
- $" return false;");
- }
-
- _writer.WriteLine(string.Empty);
- _writer.WriteLineIndented("return true;");
-
- CloseScope();
- }
-
- /// Writes the is exactly.
- /// Name of the export.
- /// The exported elements.
- private void WriteIsExactly(
- string exportName,
- List exportedElements)
- {
- // Base implementation differs from subclasses.
- if (exportName == "Base")
- {
- _writer.WriteLineIndented("public virtual bool IsExactly(IDeepComparable other) => other is Base;");
- _writer.WriteLine(string.Empty);
- return;
- }
-
- _writer.WriteLineIndented("public override bool IsExactly(IDeepComparable other)");
-
- OpenScope();
- _writer.WriteLineIndented($"var otherT = other as {exportName};");
- _writer.WriteLineIndented("if(otherT == null) return false;");
- _writer.WriteLine(string.Empty);
-
- _writer.WriteLineIndented("if(!base.IsExactly(otherT)) return false;");
-
- foreach (WrittenElementInfo info in exportedElements)
- {
- _writer.WriteLineIndented(
- info.PropertyType is CqlTypeReference
- ? $"if({info.PropertyName} != otherT.{info.PropertyName}) return false;"
- : $"if( !DeepComparable.IsExactly({info.PropertyName}, otherT.{info.PropertyName}))" +
- $" return false;");
- }
-
- _writer.WriteLine(string.Empty);
-
- if (exportName == "PrimitiveType")
- {
- _writer.WriteLineIndented("var otherValue = otherT.ObjectValue;");
-
- _writer.WriteLineIndented("if (ObjectValue is byte[] bytes && otherValue is byte[] bytesOther)");
- _writer.IncreaseIndent();
- _writer.WriteLineIndented("return Enumerable.SequenceEqual(bytes, bytesOther);");
- _writer.DecreaseIndent();
- _writer.WriteLineIndented("else");
- _writer.IncreaseIndent();
- _writer.WriteLineIndented("return Equals(ObjectValue, otherT.ObjectValue);");
- _writer.DecreaseIndent();
+ _writer.WriteLineIndented("return Equals(JsonValue, otherT.JsonValue);");
_writer.WriteLine(string.Empty);
}
else
@@ -2437,15 +2343,13 @@ private void WriteCopyTo(
List exportedElements)
{
var specifier = exportName == "Base" ? "virtual" : "override";
- _writer.WriteLineIndented($"public {specifier} IDeepCopyable CopyTo(IDeepCopyable other)");
- OpenScope();
- _writer.WriteLineIndented($"var dest = other as {exportName};");
- _writer.WriteLine(string.Empty);
-
- _writer.WriteLineIndented("if (dest == null)");
+ _writer.WriteLineIndented($"protected internal {specifier} void CopyToInternal(Base other)");
OpenScope();
+ _writer.WriteLineIndented($"if(other is not {exportName} dest)");
+ _writer.IncreaseIndent();
_writer.WriteLineIndented("throw new ArgumentException(\"Can only copy to an object of the same type\", \"other\");");
- CloseScope();
+ _writer.DecreaseIndent();
+ _writer.WriteLine();
if (exportName == "Base")
{
@@ -2454,10 +2358,14 @@ private void WriteCopyTo(
_writer.WriteLineIndented("dest.annotations.AddRange(annotations);");
_writer.DecreaseIndent();
_writer.WriteLine(string.Empty);
+ _writer.WriteLineIndented("if (HasOverflow)");
+ _writer.IncreaseIndent();
+ _writer.WriteLineIndented("Overflow.CopyToInternal(dest.Overflow);");
+ _writer.DecreaseIndent();
}
else
{
- _writer.WriteLineIndented("base.CopyTo(dest);");
+ _writer.WriteLineIndented("base.CopyToInternal(dest);");
}
foreach (WrittenElementInfo info in exportedElements)
@@ -2465,23 +2373,21 @@ private void WriteCopyTo(
if (info.PropertyType is ListTypeReference)
{
_writer.WriteLineIndented(
- $"if({info.PropertyName}.Any())" +
- $" dest.{info.PropertyName} = new {info.PropertyType.PropertyTypeString}({info.PropertyName}.DeepCopy());");
+ $"if(_{info.PropertyName} is not null)" +
+ $" dest.{info.PropertyName} = new {info.PropertyType.PropertyTypeString}(_{info.PropertyName}.DeepCopyInternal());");
}
else
{
_writer.WriteLineIndented(
- $"if({info.PropertyName} != null) dest.{info.PropertyName} = " +
+ $"if(_{info.PropertyName} is not null) dest.{info.PropertyName} = " +
(info.PropertyType is CqlTypeReference ?
- $"{info.PropertyName};" :
- $"({info.PropertyType.PropertyTypeString}){info.PropertyName}.DeepCopy();"));
+ $"_{info.PropertyName};" :
+ $"({info.PropertyType.PropertyTypeString})_{info.PropertyName}.DeepCopyInternal();"));
}
}
if (exportName == "PrimitiveType")
- _writer.WriteLineIndented("if (ObjectValue != null) dest.ObjectValue = ObjectValue;");
-
- _writer.WriteLineIndented("return dest;");
+ _writer.WriteLineIndented("if (JsonValue != null) dest.JsonValue = JsonValue;");
CloseScope();
}
@@ -2494,17 +2400,16 @@ private void WriteDeepCopy(
// Base implementation differs from subclasses.
if (exportName == "Base")
{
- _writer.WriteLineIndented("public virtual IDeepCopyable DeepCopy() =>");
- _writer.IncreaseIndent();
- _writer.WriteLineIndented("CopyTo((IDeepCopyable)Activator.CreateInstance(GetType())!);");
- _writer.DecreaseIndent();
+ _writer.WriteLineIndented("protected internal abstract Base DeepCopyInternal();");
_writer.WriteLine(string.Empty);
return;
}
- _writer.WriteLineIndented("public override IDeepCopyable DeepCopy()");
+ _writer.WriteLineIndented("protected internal override Base DeepCopyInternal()");
OpenScope();
- _writer.WriteLineIndented($"return CopyTo(new {exportName}());");
+ _writer.WriteLineIndented($"var instance = new {exportName}();");
+ _writer.WriteLineIndented("CopyToInternal(instance);");
+ _writer.WriteLineIndented("return instance;");
CloseScope();
}
@@ -2523,11 +2428,14 @@ private void WriteConstrainedQuantity(
// open class
OpenScope();
- WritePropertyTypeName(complex.Structure.Name);
+ if(complex.Structure.Abstract != true)
+ WritePropertyTypeName(complex.Structure.Name);
- _writer.WriteLineIndented("public override IDeepCopyable DeepCopy()");
+ _writer.WriteLineIndented("protected internal override Base DeepCopyInternal()");
OpenScope();
- _writer.WriteLineIndented($"return CopyTo(new {exportName}());");
+ _writer.WriteLineIndented($"var instance = new {exportName}();");
+ _writer.WriteLineIndented("CopyToInternal(instance);");
+ _writer.WriteLineIndented("return instance;");
CloseScope();
//_writer.WriteLineIndented("// TODO: Add code to enforce these constraints:");
@@ -2559,50 +2467,16 @@ private void WriteBackboneComponent(
WriteComponentComment(complex);
- string explicitName = complex.cgExplicitName();
-
- /* TODO(ginoc): 2024.06.28 - Special cases to remove in SDK 6.0
- * - Evidence.statistic.attributeEstimate.attributeEstimate the explicit name is duplicative and was not passed through.
- * - Citation.citedArtifact.contributorship.summary had a generator prefix.
- */
- switch (explicitName)
- {
- case "AttributeEstimateAttributeEstimate":
- explicitName = "AttributeEstimate";
- break;
- case "ContributorshipSummary":
- explicitName = "CitedArtifactContributorshipSummary";
- break;
- }
-
- // ginoc 2024.03.12: Release has happened and these are no longer needed - leaving here but commented out until confirmed
- /*
- // TODO: the following renames (repairs) should be removed when release 4B is official and there is an
- // explicit name in the definition for attributes:
- // - Statistic.attributeEstimate.attributeEstimate
- // - Citation.contributorship.summary
-
- if (complex.Id.StartsWith("Citation") || complex.Id.StartsWith("Statistic") || complex.Id.StartsWith("DeviceDefinition"))
- {
- string parentName = complex.Id.Substring(0, complex.Id.IndexOf('.'));
- var sillyBackboneName = complex.Id.Substring(parentName.Length);
- explicitName = capitalizeThoseSillyBackboneNames(sillyBackboneName);
- exportName = explicitName + "Component";
- }
- // end of repair
- */
+ string? explicitName = GetExplicitName(complex.Element, _info.FhirSequence);
bool useConcatenationInName = complex.Structure.Name == "Citation";
- string explicitNamePart = string.IsNullOrEmpty(explicitName)
- ? complex.cgName(NamingConvention.PascalCase, useConcatenationInName, useConcatenationInName)
- : explicitName;
- string componentName = parentExportName + "#" + explicitNamePart;
+ string explicitNamePart = explicitName ??
+ complex.cgName(NamingConvention.PascalCase, useConcatenationInName, useConcatenationInName);
+ string componentName = complex.Element.Path;
WriteSerializable();
- _writer.WriteLineIndented($"[FhirType(\"{componentName}\", IsNestedType=true)]");
-
- _writer.WriteLineIndented($"[BackboneType(\"{complex.Element.Path}\")]");
+ _writer.WriteLineIndented($"[FhirType(\"{componentName}\", IsBackboneType=true)]");
_writer.WriteLineIndented(
$"public partial class" +
@@ -2612,7 +2486,8 @@ private void WriteBackboneComponent(
// open class
OpenScope();
- WritePropertyTypeName(componentName);
+ if(complex.Structure.Abstract != true)
+ WritePropertyTypeName(componentName);
WriteElements(complex, exportName, ref exportedElements, subset);
@@ -2625,11 +2500,8 @@ private void WriteBackboneComponent(
if (exportedElements.Count > 0)
{
- WriteMatches(exportName, exportedElements);
- WriteIsExactly(exportName, exportedElements);
- WriteChildren(exportName, exportedElements);
- WriteNamedChildren(exportName, exportedElements);
- WriteIDictionarySupport(exportName, exportedElements);
+ WriteCompareChildren(exportName, exportedElements);
+ WriteDictionarySupport(exportName, exportedElements);
}
// close class
@@ -2639,53 +2511,15 @@ private void WriteBackboneComponent(
foreach (ComponentDefinition component in complex.cgChildComponents(_info))
{
string componentExportName;
- string componentExplicitName = component.cgExplicitName();
- if (string.IsNullOrEmpty(componentExplicitName))
+ if (GetExplicitName(component.Element, _info.FhirSequence) is {} componentExplicitName)
{
- componentExportName =
- $"{component.cgName(NamingConvention.PascalCase, useConcatenationInName, useConcatenationInName)}Component";
+ componentExportName = $"{componentExplicitName}Component";
}
else
{
- /* TODO(ginoc): 2024.06.28 - Special cases to remove in SDK 6.0
- * - Evidence.statistic.attributeEstimate.attributeEstimate the explicit name is duplicative and was not passed through.
- * - Citation.citedArtifact.contributorship.summary had a generator prefix.
- */
-
- switch (componentExplicitName)
- {
- case "AttributeEstimateAttributeEstimate":
- componentExportName = "AttributeEstimateComponent";
- break;
- case "ContributorshipSummary":
- componentExportName = "CitedArtifactContributorshipSummaryComponent";
- break;
- default:
- // Consent.provisionActorComponent is explicit lower case...
- componentExportName = $"{component.cgExplicitName()}Component";
- break;
- }
-
- ///* TODO(ginoc): 2024.06.28 - Special cases to remove in SDK 6.0
- // * - Consent.provision is explicit lower case in R4B and earlier
- // * - Consent.provision.actor is explicit lower case in R4B and earlier
- // */
- //if (_info.FhirSequence < FhirReleases.FhirSequenceCodes.R5)
- //{
- // switch (complex.Element.Path)
- // {
- // case "Consent.provision":
- // componentExportName = "provisionComponent";
- // break;
- // case "Consent.provision.actor":
- // componentExportName = "provisionActorComponent";
- // break;
- // case "Consent.provision.data":
- // componentExportName = "provisionDataComponent";
- // break;
- // }
- //}
+ componentExportName =
+ $"{component.cgName(NamingConvention.PascalCase, useConcatenationInName, useConcatenationInName)}Component";
}
WriteBackboneComponent(
@@ -2731,16 +2565,6 @@ private void WriteEnums(
WriteEnums(component, className, usedEnumNames, processedValueSets);
}
- // after processing, we need to look for value sets we are forcing in
- foreach ((string url, ValueSetBehaviorOverrides behaviors) in _valueSetBehaviorOverrides)
- {
- if (behaviors.ForceInClasses.Contains(className) &&
- _info.TryExpandVs(url, out ValueSet? vs) &&
- !processedValueSets.Contains(vs.Url))
- {
- WriteEnum(vs, className, usedEnumNames);
- }
- }
}
/// Writes a value set as an enum.
@@ -2754,40 +2578,16 @@ private bool WriteEnum(
HashSet usedEnumNames,
bool silent = false)
{
- bool passes = false;
-
- if (_valueSetBehaviorOverrides.TryGetValue(vs.Url, out ValueSetBehaviorOverrides? behaviors))
- {
- if (behaviors.ForceInClasses.Contains(className))
- {
- // skip other checks
- passes = true;
- }
- else if (behaviors.AllowInClasses == false)
- {
- return false;
- }
- }
-
- if (passes || _writtenValueSets.ContainsKey(vs.Url))
+ if (_writtenValueSets.ContainsKey(vs.Url))
{
return true;
}
- if (passes || _exclusionSet.Contains(vs.Url))
+ if (ExclusionSet.Contains(vs.Url))
{
return false;
}
- FhirConcept[] concepts = vs.cgGetFlatConcepts(_info).ToArray();
-
- if (concepts.Length == 0)
- {
- // TODO(ginoc): 2024.09.19 - do we want to start using a Terminology server to expand these?
- // value set that cannot be expanded and does not have an expansion provided
- return false;
- }
-
string name = (vs.Name ?? vs.Id)
.Replace(" ", string.Empty, StringComparison.Ordinal)
.Replace("_", string.Empty, StringComparison.Ordinal);
@@ -2796,7 +2596,7 @@ private bool WriteEnum(
// Enums and their containing classes cannot have the same name,
// so we have to correct these here
- if (_enumNamesOverride.TryGetValue(vs.Url, out var replacementName))
+ if (EnumNamesOverride.TryGetValue(vs.Url, out var replacementName))
{
nameSanitized = replacementName;
}
@@ -2812,16 +2612,22 @@ private bool WriteEnum(
{
_writtenValueSets.Add(
vs.Url,
- new WrittenValueSetInfo()
- {
- ClassName = className,
- ValueSetName = nameSanitized,
- });
+ new WrittenValueSetInfo(className, nameSanitized));
return true;
}
- IEnumerable referencedCodeSystems = vs.cgReferencedCodeSystems();
+ FhirConcept[] concepts = vs.cgGetFlatConcepts(_info).ToArray();
+
+ if (concepts.Length == 0)
+ {
+ // TODO(ginoc): 2024.09.19 - do we want to start using a Terminology server to expand these?
+ // value set that cannot be expanded and does not have an expansion provided
+ return false;
+ }
+
+
+ IEnumerable referencedCodeSystems = vs.cgReferencedCodeSystems().ToList();
if (referencedCodeSystems.Count() == 1)
{
@@ -2838,56 +2644,7 @@ private bool WriteEnum(
$"(systems: {referencedCodeSystems.Count()})");
}
- /* TODO(ginoc): 2024.07.01 - Special cases to remove in SDK 6.0
- * - ValueSet http://hl7.org/fhir/ValueSet/item-type used to enumerate non-selectable: 'question'
- * - ValueSet http://hl7.org/fhir/ValueSet/v3-ActInvoiceGroupCode in STU3 used to enumerate non-selectable: '_ActInvoiceInterGroupCode' and '_ActInvoiceRootGroupCode'
- */
- switch (vs.Url)
- {
- case "http://hl7.org/fhir/ValueSet/item-type":
- {
- if (!vs.Expansion.Contains.Any(vsContains => vsContains.Code == "question"))
- {
- vs.Expansion.Contains.Insert(2, new ValueSet.ContainsComponent()
- {
- System = "http://hl7.org/fhir/item-type",
- Code = "question",
- Display = "Question",
- });
- }
- }
- break;
-
- case "http://hl7.org/fhir/ValueSet/v3-ActInvoiceGroupCode":
- {
- // only care about the version present in STU3
- if (vs.Version == "2014-03-26")
- {
- if (!vs.Expansion.Contains.Any(vsContains => vsContains.Code == "_ActInvoiceInterGroupCode"))
- {
- vs.Expansion.Contains.Insert(0, new ValueSet.ContainsComponent()
- {
- System = "http://hl7.org/fhir/v3/ActCode",
- Code = "_ActInvoiceInterGroupCode",
- Display = "ActInvoiceInterGroupCode",
- });
- }
-
- if (!vs.Expansion.Contains.Any(vsContains => vsContains.Code == "_ActInvoiceRootGroupCode"))
- {
- vs.Expansion.Contains.Insert(8, new ValueSet.ContainsComponent()
- {
- System = "http://hl7.org/fhir/v3/ActCode",
- Code = "_ActInvoiceRootGroupCode",
- Display = "ActInvoiceRootGroupCode",
- });
- }
- }
- }
- break;
- }
-
- var defaultSystem = GetDefaultCodeSystem(concepts);
+ string defaultSystem = GetDefaultCodeSystem(concepts);
_writer.WriteLineIndented($"[FhirEnumeration(\"{name}\", \"{vs.Url}\", \"{defaultSystem}\")]");
@@ -2899,6 +2656,9 @@ private bool WriteEnum(
foreach (FhirConcept concept in concepts)
{
+ if (concept.IsAbstract is true)
+ continue;
+
string codeName = ConvertEnumValue(concept.Code);
string codeValue = FhirSanitizationUtils.SanitizeForValue(concept.Code);
string description = string.IsNullOrEmpty(concept.Definition)
@@ -2943,15 +2703,52 @@ private bool WriteEnum(
_writer.WriteLineIndented($"{codeName},");
}
+ // HACK for short-term R6 support....
+ // We need to add the FHIR version enum literals for R6, as they are not part of the
+ // R5 distribution, which we are using at this moment to generate Base/Conformance.
+ if (vs.Url == "http://hl7.org/fhir/ValueSet/FHIR-version")
+ {
+ _writer.WriteIndented(
+ """
+ ///
+ /// R6 Versions.
+ /// (system: http://hl7.org/fhir/FHIR-version)
+ ///
+ [EnumLiteral("6.0"), Description("6.0")]
+ N6_0,
+ ///
+ /// R6 Final Version.
+ /// (system: http://hl7.org/fhir/FHIR-version)
+ ///
+ [EnumLiteral("6.0.0"), Description("6.0.0")]
+ N6_0_0,
+ ///
+ /// R6 1st Draft Ballot.
+ /// (system: http://hl7.org/fhir/FHIR-version)
+ ///
+ [EnumLiteral("6.0.0-ballo1"), Description("6.0.0-ballot1")]
+ N6_0_0Ballo1,
+ ///
+ /// R6 2nd Draft Ballot.
+ /// (system: http://hl7.org/fhir/FHIR-version)
+ ///
+ [EnumLiteral("6.0.0-ballot2"), Description("6.0.0-ballot2")]
+ N6_0_0Ballot2,
+ ///
+ /// R6 3rd Draft Ballot.
+ /// (system: http://hl7.org/fhir/FHIR-version)
+ ///
+ [EnumLiteral("6.0.0-ballot3"), Description("6.0.0-ballot3")]
+ N6_0_0Ballot3,
+ """);
+ }
+
+
CloseScope();
_writtenValueSets.Add(
vs.Url,
- new WrittenValueSetInfo()
- {
- ClassName = className,
- ValueSetName = nameSanitized,
- });
+ new WrittenValueSetInfo(className, nameSanitized));
return true;
}
@@ -3008,15 +2805,22 @@ private void WriteElements(
orderOffset);
}
}
- private void BuildFhirElementAttribute(string name, string summary, string? isModifier, ElementDefinition element, int orderOffset, string choice, string fiveWs, string? since = null, (string, string)? until = null, string? xmlSerialization = null)
+ private void WriteFhirElementAttribute(string name, string summary, string? isModifier, ElementDefinition element,
+ string choice, string fiveWs, string? since = null, (string, string)? until = null,
+ string? xmlSerialization = null, string? declaredType = null)
{
var xmlser = xmlSerialization is null ? null : $", XmlSerialization = XmlRepresentation.{xmlSerialization}";
string attributeText = $"[FhirElement(\"{name}\"{xmlser}{summary}{isModifier}, Order={GetOrder(element)}{choice}{fiveWs}";
- if (since is { })
+ if (since is not null)
{
attributeText += $", Since=FhirRelease.{since}";
}
+ if (declaredType is not null)
+ {
+ attributeText += $", DeclaredType={declaredType}";
+ }
+
attributeText += ")]";
_writer.WriteLineIndented(attributeText);
@@ -3062,72 +2866,42 @@ private void WriteElement(
}
string path = element.cgPath();
-
- var since = _sinceAttributes.GetValueOrDefault(path);
- var until = _untilAttributes.TryGetValue(path, out (string, string) u) ? u : default((string, string)?);
+ string? since = _sinceAttributes.GetValueOrDefault(path);
+ (string, string)? until = _untilAttributes.TryGetValue(path, out (string, string) u) ? u : default((string, string)?);
// TODO: Modify these elements in ModifyDefinitionsForConsistency
- var description = path switch
+ string? remarks = path switch
{
- "Signature.who" => element.Short + ".\nNote 1: Since R4 the type of this element should be a fixed type (ResourceReference). For backwards compatibility it remains of type DataType.\nNote 2: Since R5 the cardinality is expanded to 0..1 (previous it was 1..1).",
- "Signature.onBehalfOf" => element.Short + ".\nNote: Since R4 the type of this element should be a fixed type (ResourceReference). For backwards compatibility it remains of type DataType.",
- "Signature.when" => element.Short + ".\nNote: Since R5 the cardinality is expanded to 0..1 (previous it was 1..1).",
- "Signature.type" => element.Short + ".\nNote: Since R5 the cardinality is expanded to 0..* (previous it was 1..*).",
- _ => AttributeDescriptionWithSinceInfo(name, element.Short, since, until)
+ "Signature.who" => "Note 1: Since R4 the type of this element should be a fixed type (ResourceReference). For backwards compatibility it remains of type DataType.\nNote 2: Since R5 the cardinality is expanded to 0..1 (previous it was 1..1).",
+ "Signature.onBehalfOf" => "Since R4 the type of this element should be a fixed type (ResourceReference). For backwards compatibility it remains of type DataType.",
+ "Signature.when" => "Since R5 the cardinality is expanded to 0..1 (previous it was 1..1).",
+ "Signature.type" => "Since R5 the cardinality is expanded to 0..* (previous it was 1..*).",
+ _ => MakeAttributeRemarkForNewOrDeprecatedProperties(name, null, since, until)
};
+ if(element.Short is not null) WriteIndentedComment(element.Short.EnsurePeriod());
+ remarks = MakeAttributeRemarkForChangedTypes(path, remarks);
+ if (remarks is not null) WriteIndentedComment(remarks, isSummary: false, isRemarks: true);
+
string? xmlSerialization = path == "Narrative.div" ? "XHtml" :
+ path is "Extension.url" or "Element.id" ? "XmlAttr" :
ei.PropertyType is CqlTypeReference ? "XmlAttr" :
null;
- if (description is not null) WriteIndentedComment(description);
-
if (path == "OperationOutcome.issue.severity")
{
- BuildFhirElementAttribute(name, summary, ", IsModifier=true", element, orderOffset, choice, fiveWs);
- BuildFhirElementAttribute(name, summary, null, element, orderOffset, choice, fiveWs, since: "R4");
+ WriteFhirElementAttribute(name, summary, ", IsModifier=true", element, choice, fiveWs);
+ WriteFhirElementAttribute(name, summary, null, element, choice, fiveWs, since: "R4");
}
else if (path is "Signature.who" or "Signature.onBehalfOf")
{
- BuildFhirElementAttribute(name, summary, isModifier, element, orderOffset, ", Choice = ChoiceType.DatatypeChoice", fiveWs);
- BuildFhirElementAttribute(name, summary, isModifier, element, orderOffset, "", fiveWs, since: since);
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof(ResourceReference), Since = FhirRelease.R4)]");
+ WriteFhirElementAttribute(name, summary, isModifier, element, ", Choice = ChoiceType.DatatypeChoice", fiveWs);
+ WriteFhirElementAttribute(name, summary, isModifier, element, "", fiveWs, since: since);
+ _writer.WriteLineIndented($"[AllowedTypes(typeof(ResourceReference), Since = FhirRelease.R4)]");
}
else
{
- BuildFhirElementAttribute(name, summary, isModifier, element, orderOffset, choice, fiveWs, since, until, xmlSerialization);
- }
-
- if (ei.PropertyType is CqlTypeReference ctr)
- {
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof({ctr.DeclaredTypeString}))]");
- }
- else if (path == "Meta.profile")
- {
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof(Canonical), Since = FhirRelease.R4)]");
- }
- else if (path == "Bundle.link.relation")
- {
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof(Code), Since = FhirRelease.R5)]");
- }
- else if (path == "Attachment.size")
- {
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof(UnsignedInt), Since = FhirRelease.STU3)]");
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof(Integer64), Since = FhirRelease.R5)]");
- }
- else if (path is
- "ElementDefinition.constraint.requirements" or
- "ElementDefinition.binding.description" or
- "ElementDefinition.mapping.comment" or
- "CapabilityStatement.implementation.description")
- {
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof(FhirString))]");
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof(Markdown), Since = FhirRelease.R5)]");
- }
-
- if (TryGetPrimitiveType(ei.PropertyType, out var ptr) && ptr is CodedTypeReference)
- {
- _writer.WriteLineIndented("[DeclaredType(Type = typeof(Code))]");
+ WriteFhirElementAttribute(name, summary, isModifier, element, choice, fiveWs, since, until, xmlSerialization);
}
if (!string.IsNullOrEmpty(element.cgBindingName()))
@@ -3135,8 +2909,13 @@ private void WriteElement(
_writer.WriteLineIndented($"[Binding(\"{element.cgBindingName()}\")]");
}
- if (element.cgIsSimple() && element.Type.Count == 1 && element.Type.Single().cgName() == "uri")
- _writer.WriteLineIndented("[UriPattern]");
+ if (_elementTypeChanges.TryGetValue(path, out ElementTypeChange[]? changes))
+ {
+ foreach(ElementTypeChange change in changes)
+ {
+ _writer.WriteLineIndented(BuildAllowedTypesAttribute([change.DeclaredTypeReference], change.Since));
+ }
+ }
bool notClsCompliant = !string.IsNullOrEmpty(allowedTypes) ||
!string.IsNullOrEmpty(resourceReferences);
@@ -3171,34 +2950,51 @@ private void WriteElement(
_writer.WriteLineIndented($"[Cardinality(Min={element.Min},Max={element.cgCardinalityMax()})]");
}
- writeElementGettersAndSetters(element, ei);
+ WriteElementGettersAndSetters(element, ei);
}
- private static string? AttributeDescriptionWithSinceInfo(string name, string baseDescription, string? since = null, (string, string)? until = null)
+ private static string? MakeAttributeRemarkForNewOrDeprecatedProperties(string name, string? baseRemark, string? since = null, (string, string)? until = null)
{
- return (since, until, baseDescription) switch
+ var deprecationRemark = (since, until, baseRemark) switch
{
- (_, _, null) => null,
- (not null, _, _) => baseDescription +
- $". Note: Element was introduced in {since}, do not use when working with older releases.",
- (_, (var release, ""), _) => baseDescription +
- $". Note: Element is deprecated since {release}, do not use with {release} and newer releases.",
- (_, (var release, var replacedBy), _) => baseDescription +
- $". Note: Element is replaced by '{replacedBy}' since {release}. Do not use this element '{name}' with {release} and newer releases.",
- _ => baseDescription
+ (not null, _, _) => $"Element was introduced in {since}, do not use when working with older releases.",
+ (_, (var release, ""), _) => $"Element is deprecated since {release}, do not use with {release} and newer releases.",
+ (_, (var release, var replacedBy), _) => $"Element is replaced by '{replacedBy}' since {release}. Do not use this element '{name}' with {release} and newer releases.",
+ _ => null
};
+
+ if(baseRemark is null)
+ {
+ return deprecationRemark;
+ }
+
+ return $"{baseRemark}. {deprecationRemark}";
}
- private static PrimitiveTypeReference BuildTypeReferenceForCode(DefinitionCollection info, ElementDefinition element, Dictionary writtenValueSets)
+ private static string? MakeAttributeRemarkForChangedTypes(string path, string? baseDescription)
+ {
+ if(!_elementTypeChanges.TryGetValue(path, out ElementTypeChange[]? changes))
+ {
+ return baseDescription;
+ }
+
+ string changedDescription = $"The type of this element has changed over time. Make sure to use "
+ + string.Join(", ",
+ changes.Select(change => $"{change.DeclaredTypeReference.PropertyTypeString} {VersionChangeMessage(changes, change, false)}")) + ".";
+
+ return baseDescription is null ? changedDescription : $"{baseDescription}. {changedDescription}";
+ }
+
+ private static (string? enumName, string? enumClass) GetVsInfoForCodedElement(DefinitionCollection info, ElementDefinition element, Dictionary writtenValueSets)
{
if ((element.Binding?.Strength != Hl7.Fhir.Model.BindingStrength.Required) ||
(!info.TryExpandVs(element.Binding.ValueSet, out ValueSet? vs)) ||
- _exclusionSet.Contains(vs.Url) ||
+ ExclusionSet.Contains(vs.Url) ||
(_codedElementOverrides.Contains(element.Path) && info.FhirSequence >= FhirReleases.FhirSequenceCodes.R4) ||
- !writtenValueSets.TryGetValue(vs.Url, out WrittenValueSetInfo vsInfo))
+ !writtenValueSets.TryGetValue(vs.Url, out WrittenValueSetInfo? vsInfo))
{
- return PrimitiveTypeReference.GetTypeReference("code");
+ return (null, null);
}
string vsClass = vsInfo.ClassName;
@@ -3206,7 +3002,7 @@ private static PrimitiveTypeReference BuildTypeReferenceForCode(DefinitionCollec
if (string.IsNullOrEmpty(vsClass))
{
- return new CodedTypeReference(vsName, null);
+ return (vsName, null);
}
string pascal = element.cgName().ToPascalCase();
@@ -3217,7 +3013,7 @@ private static PrimitiveTypeReference BuildTypeReferenceForCode(DefinitionCollec
$"Change the name of the valueset '{vs.Url}' by adapting the _enumNamesOverride variable in the generator and rerun.");
}
- return new CodedTypeReference(vsName, vsClass);
+ return (vsName, vsClass);
}
private static TypeReference DetermineTypeReferenceForFhirElement(
@@ -3232,38 +3028,26 @@ private static TypeReference DetermineTypeReferenceForFhirElement(
TypeReference determineTypeReferenceForFhirElementName()
{
- if (element.Path is "Meta.profile")
+ if(_elementTypeChanges.TryGetValue(element.Path, out ElementTypeChange[]? changes))
{
- /* we want to share Meta across different FHIR versions,
- * so we use the "most common" type to the versions, which
- * is uri rather than the more specific canonical. */
- return PrimitiveTypeReference.GetTypeReference("uri");
- }
+ // If the element has a type change, we need to use DataType, to make
+ // sure the property can capture all the types.
+ if(changes.All(c => c.DeclaredTypeReference is PrimitiveTypeReference))
+ {
+ return PrimitiveTypeReference.PrimitiveType;
+ }
- if (element.Path is "Element.id" or "Extension.url")
- {
- /* these two properties formally use a CQL primitive (at least,
- * that's how they are encoded in the StructureDefinition. */
- return CqlTypeReference.SystemString;
+ return ComplexTypeReference.DataTypeReference;
}
- var initialTypeName = getTypeNameFromElement();
-
- // Elements that use multiple datatypes are of type DataType
- // TODO: Probably need the list of types later to be able to render the
- // AllowedTypes.
- if (initialTypeName == "DataType")
- return new ChoiceTypeReference();
+ string initialTypeName = getTypeNameFromElement();
// Elements of type Code or Code have their own naming/types, so handle those separately.
- if (initialTypeName == "code")
- return BuildTypeReferenceForCode(info, element, writtenValueSets);
-
- if (PrimitiveTypeReference.IsFhirPrimitiveType(initialTypeName))
- return PrimitiveTypeReference.GetTypeReference(initialTypeName);
+ var (vsName,vsClass) = initialTypeName == "code"
+ ? GetVsInfoForCodedElement(info, element, writtenValueSets)
+ : (null,null);
- // Otherwise, this is a "normal" name for a complex type.
- return new ComplexTypeReference(initialTypeName, getPocoNameForComplexTypeReference(initialTypeName));
+ return TypeReference.BuildFromFhirTypeName(initialTypeName, vsName, vsClass);
string getTypeNameFromElement()
{
@@ -3272,9 +3056,9 @@ string getTypeNameFromElement()
{
// TODO(ginoc): this should move into cgBaseTypeName();
// check to see if the referenced element has an explicit name
- if (info.TryFindElementByPath(btn, out StructureDefinition? targetSd, out ElementDefinition? targetEd))
+ if (info.TryFindElementByPath(btn, out StructureDefinition? _, out ElementDefinition? targetEd))
{
- return BuildTypeNameForNestedComplexType(targetEd, btn);
+ return BuildTypeNameForNestedComplexType(targetEd, btn, info.FhirSequence);
}
return btn;
@@ -3284,32 +3068,23 @@ string getTypeNameFromElement()
? element.Type.First().cgName()
: "DataType";
}
-
- string getPocoNameForComplexTypeReference(string name)
- {
- return name.Contains('.')
- ? BuildTypeNameForNestedComplexType(element, name)
- : TypeReference.MapTypeName(name);
- }
}
}
internal static bool TryGetPrimitiveType(TypeReference tr, [NotNullWhen(true)] out PrimitiveTypeReference? ptr)
{
- if (tr is PrimitiveTypeReference p)
- {
- ptr = p;
- return true;
- }
-
- if (tr is ListTypeReference { Element: PrimitiveTypeReference pltr })
+ switch (tr)
{
- ptr = pltr;
- return true;
+ case PrimitiveTypeReference p:
+ ptr = p;
+ return true;
+ case ListTypeReference { Element: PrimitiveTypeReference pltr }:
+ ptr = pltr;
+ return true;
+ default:
+ ptr = null;
+ return false;
}
-
- ptr = null;
- return false;
}
internal WrittenElementInfo BuildElementInfo(
@@ -3341,103 +3116,157 @@ internal static WrittenElementInfo BuildElementInfo(
PropertyType: typeRef,
PrimitiveHelperName: forPrimitiveType
? (pascal == exportedComplexName ? $"{pascal}_" : pascal)
- : null // Since properties cannot have the same name as their enclosing types, we'll add a '_' suffix if this happens.
+ : null, // Since properties cannot have the same name as their enclosing types, we'll add a '_' suffix if this happens.
+ Required: element.Min > 0
);
}
- private void writeElementGettersAndSetters(ElementDefinition element, WrittenElementInfo ei)
+ private void WriteElementGettersAndSetters(ElementDefinition element, WrittenElementInfo ei)
{
_writer.WriteLineIndented("[DataMember]");
- if (ei.PropertyType is not ListTypeReference)
+ string overflowTypeName = ei.PropertyType.PropertyTypeString switch
{
- _writer.WriteLineIndented($"public {ei.PropertyType.PropertyTypeString} {ei.PropertyName}");
+ "Hl7.Fhir.Model.Resource" => "DynamicResource",
+ "Hl7.Fhir.Model.DataType" => "DynamicDataType",
+ "Hl7.Fhir.Model.PrimitiveType" => "DynamicPrimitive",
+ { } s => s
+ };
- OpenScope();
- _writer.WriteLineIndented($"get {{ return _{ei.PropertyName}; }}");
- _writer.WriteLineIndented($"set {{ _{ei.PropertyName} = value; OnPropertyChanged(\"{ei.PropertyName}\"); }}");
- CloseScope();
+ if (ei.PropertyType is ListTypeReference)
+ _writer.WriteLineIndented("[AllowNull]");
+ _writer.WriteLineIndented($"public {(ei.PropertyType is ListTypeReference || ei.Required ? ei.PropertyType.PropertyTypeString : $"{ei.PropertyType.PropertyTypeString}?")} {ei.PropertyName}");
- _writer.WriteLineIndented($"private {ei.PropertyType.PropertyTypeString} _{ei.PropertyName};");
- _writer.WriteLine(string.Empty);
- }
- else
- {
- _writer.WriteLineIndented($"public {ei.PropertyType.PropertyTypeString} {ei.PropertyName}");
+ OpenScope();
- OpenScope();
- _writer.WriteLineIndented($"get {{ if(_{ei.PropertyName}==null) _{ei.PropertyName} =" +
- $" new {ei.PropertyType.PropertyTypeString}(); return _{ei.PropertyName}; }}");
- _writer.WriteLineIndented($"set {{ _{ei.PropertyName} = value; OnPropertyChanged(\"{ei.PropertyName}\"); }}");
- CloseScope();
+ _writer.WriteLineIndented("get");
+ OpenScope();
+ _writer.WriteLineIndented($"if(_{ei.PropertyName}.InOverflow<{overflowTypeName}>())");
+ _writer.IncreaseIndent();
+ _writer.WriteLineIndented($"throw CodedValidationException.FromTypes(typeof({ei.PropertyType.PropertyTypeString}), Overflow[\"{ei.FhirElementName}\"]);");
+ _writer.DecreaseIndent();
+ _writer.WriteLineIndented(ei.PropertyType is not ListTypeReference ? $"return _{ei.PropertyName}{(ei.Required ? "!" : "")};" : $"return _{ei.PropertyName} ??= [];");
- _writer.WriteLineIndented($"private {ei.PropertyType.PropertyTypeString} _{ei.PropertyName};");
- _writer.WriteLine(string.Empty);
- }
+ CloseScope();
+
+ _writer.WriteLineIndented("set");
+ OpenScope();
+ _writer.WriteLineIndented($"if (_{ei.PropertyName}.InOverflow<{overflowTypeName}>())");
+ _writer.IncreaseIndent();
+ _writer.WriteLineIndented($"Overflow.Remove(\"{ei.FhirElementName}\");");
+ _writer.DecreaseIndent();
+ _writer.WriteLineIndented($"_{ei.PropertyName} = value;");
+ _writer.WriteLineIndented($"OnPropertyChanged(\"{ei.PropertyName}\");");
+ CloseScope();
- bool needsPrimitiveProperty = ei.PropertyType is
+ CloseScope();
+ _writer.WriteLineIndented($"private {ei.PropertyType.PropertyTypeString}? _{ei.PropertyName};");
+ _writer.WriteLine(string.Empty);
+
+ bool needsHelperProperty = ei.PropertyType is
PrimitiveTypeReference or
ListTypeReference { Element: PrimitiveTypeReference };
- if (!needsPrimitiveProperty)
+ if (needsHelperProperty)
{
- return;
+ // If the property has had multiple types over time, we need to generate a helper property for each type.
+ if(_elementTypeChanges.TryGetValue(element.Path, out ElementTypeChange[]? changes))
+ {
+ ElementTypeChange lastChange = changes.Last();
+
+ foreach(ElementTypeChange change in changes)
+ {
+ // The DeclaredType given by the maintainer is the type of the element, even if it repeats,
+ // so let's wrap that type in a list if applicable.
+ TypeReference propType = ei.PropertyType is ListTypeReference ?
+ new ListTypeReference(change.DeclaredTypeReference) : change.DeclaredTypeReference;
+ string helperName = change == lastChange
+ ? ei.PrimitiveHelperName!
+ : $"{ei.PrimitiveHelperName}{change.DeclaredTypeReference.Name.ToPascalCase()}";
+ string versionsRemark = $"Use this property {VersionChangeMessage(changes, change, false)}.";
+ WritePrimitiveHelperProperty(element.Short, ei, propType, helperName, versionsRemark);
+ }
+ }
+ else
+ {
+ WritePrimitiveHelperProperty(element.Short, ei, ei.PropertyType, ei.PrimitiveHelperName!);
+ }
}
+ }
- WriteIndentedComment(element.Short);
- _writer.WriteLineIndented($"/// This uses the native .NET datatype, rather than the FHIR equivalent");
+ private void WritePrimitiveHelperProperty(string description, WrittenElementInfo ei,
+ TypeReference? propType, string helperPropName, string? versionsRemark = null)
+ {
+ string descriptionText = versionsRemark is null
+ ? description
+ : $"{description}. {versionsRemark}";
+ WriteIndentedComment(descriptionText);
+ _writer.WriteLineIndented("/// This uses the native .NET datatype, rather than the FHIR equivalent");
_writer.WriteLineIndented("[IgnoreDataMember]");
- if (ei.PropertyType is PrimitiveTypeReference ptr)
+ switch (propType)
{
- _writer.WriteLineIndented($"public {ptr.ConveniencePropertyTypeString} {ei.PrimitiveHelperName}");
-
- OpenScope();
- _writer.WriteLineIndented($"get {{ return {ei.PropertyName} != null ? {ei.PropertyName}.Value : null; }}");
- _writer.WriteLineIndented("set");
- OpenScope();
+ case PrimitiveTypeReference ptr:
+ string typeString = WithNullabilityMarking(ptr.ConveniencePropertyTypeString);
+ _writer.WriteLineIndented($"public {typeString} {helperPropName}");
- _writer.WriteLineIndented($"if (value == null)");
+ OpenScope();
+ string propAccess = versionsRemark is not null
+ ? $"(({MostGeneralValueAccessorType(ptr)}?){ei.PropertyName})"
+ : $"{ei.PropertyName}";
+ _writer.WriteLineIndented($"get => {propAccess}?.Value;");
- _writer.IncreaseIndent();
- _writer.WriteLineIndented($"{ei.PropertyName} = null;");
- _writer.DecreaseIndent();
- _writer.WriteLineIndented("else");
- _writer.IncreaseIndent();
- _writer.WriteLineIndented($"{ei.PropertyName} = new {ei.PropertyType.PropertyTypeString}(value);");
- _writer.DecreaseIndent();
- _writer.WriteLineIndented($"OnPropertyChanged(\"{ei.PrimitiveHelperName}\");");
- CloseScope(suppressNewline: true);
- CloseScope();
+ _writer.WriteLineIndented("set");
+ OpenScope();
+ _writer.WriteLineIndented($"{ei.PropertyName} = value is null ? null! : new {ptr.PropertyTypeString}(value);");
+ _writer.WriteLineIndented($"OnPropertyChanged(\"{helperPropName}\");");
+ CloseScope(suppressNewline: true);
+ CloseScope();
+ break;
+ case ListTypeReference { Element: PrimitiveTypeReference lptr }:
+ string nullableTypeList = WithNullabilityMarking(lptr.ConveniencePropertyTypeString);
+ _writer.WriteLineIndented($"public IEnumerable<{nullableTypeList}> {helperPropName}");
+
+ OpenScope();
+
+ _writer.WriteIndented($"get => _{ei.PropertyName}");
+ if(versionsRemark is not null)
+ _writer.Write($"?.Cast<{MostGeneralValueAccessorType(lptr)}>()");
+ _writer.WriteLine($"?.Select(elem => elem.Value) ?? [];");
+
+ _writer.WriteLineIndented("set");
+ OpenScope();
+
+ _writer.WriteLineIndented($"if (value == null)");
+
+ _writer.IncreaseIndent();
+ _writer.WriteLineIndented($"{ei.PropertyName} = null!;");
+ _writer.DecreaseIndent();
+ _writer.WriteLineIndented("else");
+ _writer.IncreaseIndent();
+ _writer.WriteLineIndented($"{ei.PropertyName} = " +
+ $"new {ei.PropertyType.PropertyTypeString}" +
+ $"(value.Select(elem=>new {lptr.PropertyTypeString}(elem)));");
+ _writer.DecreaseIndent();
+
+ _writer.WriteLineIndented($"OnPropertyChanged(\"{helperPropName}\");");
+ CloseScope(suppressNewline: true);
+ CloseScope();
+ break;
}
- else if (ei.PropertyType is ListTypeReference { Element: PrimitiveTypeReference lptr })
- {
- _writer.WriteLineIndented($"public IEnumerable<{lptr.ConveniencePropertyTypeString}> {ei.PrimitiveHelperName}");
-
- OpenScope();
- _writer.WriteLineIndented($"get {{ return {ei.PropertyName} != null ? {ei.PropertyName}.Select(elem => elem.Value) : null; }}");
- _writer.WriteLineIndented("set");
- OpenScope();
-
- _writer.WriteLineIndented($"if (value == null)");
-
- _writer.IncreaseIndent();
- _writer.WriteLineIndented($"{ei.PropertyName} = null;");
- _writer.DecreaseIndent();
- _writer.WriteLineIndented("else");
- _writer.IncreaseIndent();
- _writer.WriteLineIndented($"{ei.PropertyName} = " +
- $"new {ei.PropertyType.PropertyTypeString}" +
- $"(value.Select(elem=>new {lptr.PropertyTypeString}(elem)));");
- _writer.DecreaseIndent();
+ }
- _writer.WriteLineIndented($"OnPropertyChanged(\"{ei.PrimitiveHelperName}\");");
- CloseScope(suppressNewline: true);
- CloseScope();
- }
+ private static string MostGeneralValueAccessorType(PrimitiveTypeReference ptr)
+ {
+ return ptr.ConveniencePropertyTypeString switch
+ {
+ "string" => "IValue",
+ _ => ptr.PropertyTypeString
+ };
}
+
///
/// Determine the type name for an element that has child elements, based on the definition and
/// the declared type.
@@ -3445,43 +3274,10 @@ PrimitiveTypeReference or
/// The ed.
/// The type.
/// A string.
- private static string BuildTypeNameForNestedComplexType(ElementDefinition ed, string type)
+ private static string BuildTypeNameForNestedComplexType(ElementDefinition ed, string type, FhirReleases.FhirSequenceCodes sequence)
{
- // ginoc 2024.03.12: Release has happened and these are no longer needed - leaving here but commented out until confirmed
- /*
- // TODO: the following renames (repairs) should be removed when release 4B is official and there is an
- // explicit name in the definition for attributes:
- // - Statistic.attributeEstimate.attributeEstimate
- // - Citation.contributorship.summary
-
- if (type.StartsWith("Citation") || type.StartsWith("Statistic") || type.StartsWith("DeviceDefinition"))
- {
- string parentName = type.Substring(0, type.IndexOf('.'));
- var sillyBackboneName = type.Substring(parentName.Length);
- type = parentName + "." + capitalizeThoseSillyBackboneNames(sillyBackboneName) + "Component";
- }
- // end of repair
- */
-
- string explicitTypeName = ed.cgExplicitName();
-
- if (!string.IsNullOrEmpty(explicitTypeName))
+ if (GetExplicitName(ed, sequence) is {} explicitTypeName)
{
- /* TODO(ginoc): 2024.06.28 - Special cases to remove in SDK 6.0
- * - Evidence.statistic.attributeEstimate.attributeEstimate the explicit name is duplicative and was not passed through.
- * - Citation.citedArtifact.contributorship.summary had a generator prefix.
- */
-
- switch (explicitTypeName)
- {
- case "AttributeEstimateAttributeEstimate":
- explicitTypeName = "AttributeEstimate";
- break;
- case "ContributorshipSummary":
- explicitTypeName = "CitedArtifactContributorshipSummary";
- break;
- }
-
string parentName = type.Substring(0, type.IndexOf('.'));
return $"{parentName}" +
$".{explicitTypeName}" +
@@ -3563,7 +3359,6 @@ internal static void BuildElementOptionalFlags(
if (elementType == "Resource")
{
choice = ", Choice=ChoiceType.ResourceChoice";
- allowedTypes = $"[AllowedTypes(typeof({Namespace}.Resource))]";
}
}
else
@@ -3586,7 +3381,7 @@ internal static void BuildElementOptionalFlags(
// present in the current version of the standard. So, in principle, we don't generate
// this attribute in the base subset, unless all types mentioned are present in the
// exception list above.
- bool isPrimitive(string name) => char.IsLower(name[0]);
+ static bool isPrimitive(string name) => char.IsLower(name[0]);
bool allTypesAvailable =
elementTypes.Keys.All(en =>
isPrimitive(en) // primitives are available everywhere
@@ -3597,44 +3392,23 @@ internal static void BuildElementOptionalFlags(
if (allTypesAvailable)
{
- StringBuilder sb = new();
- sb.Append("[AllowedTypes(");
-
- bool needsSep = false;
- foreach ((string etName, ElementDefinition.TypeRefComponent elementType) in elementTypes)
- {
- if (needsSep)
- {
- sb.Append(',');
- }
-
- needsSep = true;
-
- sb.Append("typeof(");
- sb.Append(Namespace);
- sb.Append('.');
-
- if (TypeNameMappings.TryGetValue(etName, out string? tmValue))
- {
- sb.Append(tmValue);
- }
- else
- {
- sb.Append(FhirSanitizationUtils.SanitizedToConvention(etName, NamingConvention.PascalCase));
- }
-
- sb.Append(')');
- }
-
- sb.Append(")]");
- allowedTypes = sb.ToString();
+ IEnumerable typeRefs = elementTypes.Values.Select(v => TypeReference.BuildFromFhirTypeName(v.Code));
+ allowedTypes = BuildAllowedTypesAttribute(typeRefs, null);
+ }
+ else if (elementTypes.Count > 30)
+ {
+ allowedTypes = BuildOpenAllowedTypesAttribute();
}
+ else
+ throw new InvalidOperationException("Cannot generate AllowedTypes attribute for element " +
+ $"{element.Path} with types {string.Join(", ", elementTypes.Keys)} because " +
+ $"not all types in the choice are available in the current subset ({subset}).");
}
}
if (elementTypes.Any())
{
- foreach ((string etName, ElementDefinition.TypeRefComponent elementType) in elementTypes.Where(kvp => (kvp.Key == "Reference") && kvp.Value.TargetProfile.Any()))
+ foreach ((string _, ElementDefinition.TypeRefComponent elementType) in elementTypes.Where(kvp => (kvp.Key == "Reference") && kvp.Value.TargetProfile.Any()))
{
resourceReferences = "[References(" +
string.Join(",", elementType.cgTargetProfiles().Keys.Select(name => "\"" + name + "\"")) +
@@ -3649,8 +3423,8 @@ internal static void BuildElementOptionalFlags(
private void WritePropertyTypeName(string name)
{
WriteIndentedComment("FHIR Type Name");
- var specifier = name == "Base" ? "virtual" : "override";
- _writer.WriteLineIndented($"public {specifier} string TypeName {{ get {{ return \"{name}\"; }} }}");
+
+ _writer.WriteLineIndented($"""public override string TypeName => "{name}";""");
_writer.WriteLine(string.Empty);
}
@@ -3669,7 +3443,7 @@ private void WritePrimitiveTypes(
foreach (StructureDefinition primitive in primitives.OrderBy(sd => sd.Name))
{
- if (_exclusionSet.Contains(primitive.Name))
+ if (ExclusionSet.Contains(primitive.Name))
{
continue;
}
@@ -3708,12 +3482,7 @@ private void WritePrimitiveType(
writtenModels.Add(
primitive.Name,
- new WrittenModelInfo()
- {
- FhirName = primitive.Name,
- CsName = $"{Namespace}.{exportName}",
- IsAbstract = false, // no abstract primitives
- });
+ new WrittenModelInfo(FhirName: primitive.Name, CsName: $"{Namespace}.{exportName}", IsAbstract: false));
string filename = Path.Combine(_exportDirectory, "Generated", $"{exportName}.cs");
@@ -3757,14 +3526,15 @@ private void WritePrimitiveType(
_writer.WriteLineIndented(
$"public partial class" +
- $" {exportName}" +
- $" : PrimitiveType, " +
- PrimitiveValueInterface(typeName));
+ $" {exportName}" +
+ $" : PrimitiveType, " +
+ PrimitiveValueInterface(typeName));
// open class
OpenScope();
- WritePropertyTypeName(primitive.Name);
+ if (primitive.Abstract != true)
+ WritePropertyTypeName(primitive.Name);
if (!string.IsNullOrEmpty(primitive.cgpValidationRegEx()))
{
@@ -3776,30 +3546,37 @@ private void WritePrimitiveType(
_writer.WriteLine(string.Empty);
}
- _writer.WriteLineIndented($"public {exportName}({typeName} value)");
+ var nullableTypeName = typeName.EndsWith('?') ? typeName : typeName + '?';
+
+ _writer.WriteLineIndented($"public {exportName}({nullableTypeName} value)");
OpenScope();
_writer.WriteLineIndented("Value = value;");
CloseScope();
- _writer.WriteLineIndented($"public {exportName}(): this(({typeName})null) {{}}");
+ _writer.WriteLineIndented($"public {exportName}(): this(({nullableTypeName})null) {{}}");
_writer.WriteLine(string.Empty);
- WriteIndentedComment("Primitive value of the element");
+ // For some primitive pocos, we need a hand-written value propery since we are using
+ // a different value type for JsonValue and Value.
+ if (exportName is not ("Integer64" or "Base64Binary" or "Instant"))
+ {
+ WriteIndentedComment("Primitive value of the element");
+
+ _writer.WriteLineIndented(
+ "[FhirElement(\"value\", IsPrimitiveValue=true, XmlSerialization=XmlRepresentation.XmlAttr, InSummary=true, Order=30)]");
- _writer.WriteLineIndented("[FhirElement(\"value\", IsPrimitiveValue=true, XmlSerialization=XmlRepresentation.XmlAttr, InSummary=true, Order=30)]");
- _writer.WriteLineIndented($"[DeclaredType(Type = typeof({getSystemTypeForFhirType(primitive.Name)}))]");
+ _writer.WriteLineIndented("[DataMember]");
- if (PrimitiveValidationPatterns.TryGetValue(primitive.Name, out string? primitivePattern))
- {
- _writer.WriteLineIndented($"[{primitivePattern}]");
+ _writer.WriteLineIndented($"public {nullableTypeName} Value");
+ OpenScope();
+
+ var typeNameInSwitch = typeName.EndsWith("?") ? typeName[..^1] : typeName;
+ _writer.WriteLineIndented($"get {{ return JsonValue is {typeNameInSwitch} or null ? ({nullableTypeName})JsonValue : throw COVE.FromTypes(typeof({exportName}), JsonValue); }}");
+ _writer.WriteLineIndented("set { JsonValue = value; OnPropertyChanged(\"Value\"); }");
+ CloseScope();
}
- _writer.WriteLineIndented("[DataMember]");
- _writer.WriteLineIndented($"public {typeName} Value");
- OpenScope();
- _writer.WriteLineIndented($"get {{ return ({typeName})ObjectValue; }}");
- _writer.WriteLineIndented("set { ObjectValue = value; OnPropertyChanged(\"Value\"); }");
- CloseScope();
+ WriteDeepCopy(exportName);
// close class
CloseScope();
@@ -3894,6 +3671,7 @@ private void WriteHeaderComplexDataType()
WriteGenerationComment();
_writer.WriteLineIndented("using System;");
+ _writer.WriteLineIndented("using System.Collections;");
_writer.WriteLineIndented("using System.Collections.Generic;");
_writer.WriteLineIndented("using System.Linq;");
_writer.WriteLineIndented("using System.Runtime.Serialization;");
@@ -3902,8 +3680,12 @@ private void WriteHeaderComplexDataType()
_writer.WriteLineIndented("using Hl7.Fhir.Specification;");
_writer.WriteLineIndented("using Hl7.Fhir.Utility;");
_writer.WriteLineIndented("using Hl7.Fhir.Validation;");
+ _writer.WriteLineIndented("using System.Diagnostics.CodeAnalysis;");
_writer.WriteLineIndented("using SystemPrimitive = Hl7.Fhir.ElementModel.Types;");
- _writer.WriteLine(string.Empty);
+ _writer.WriteLine();
+
+ _writer.WriteLineIndented("#nullable enable");
+ _writer.WriteLine();
WriteCopyright();
@@ -3924,9 +3706,14 @@ private void WriteHeaderPrimitive()
_writer.WriteLineIndented("using Hl7.Fhir.Introspection;");
_writer.WriteLineIndented("using Hl7.Fhir.Specification;");
_writer.WriteLineIndented("using Hl7.Fhir.Validation;");
+ _writer.WriteLineIndented("using System.Diagnostics.CodeAnalysis;");
_writer.WriteLineIndented("using SystemPrimitive = Hl7.Fhir.ElementModel.Types;");
+ _writer.WriteLineIndented("using COVE=Hl7.Fhir.Validation.CodedValidationException;");
_writer.WriteLine(string.Empty);
+ _writer.WriteLineIndented("#nullable enable");
+ _writer.WriteLine();
+
WriteCopyright();
}
@@ -4039,21 +3826,12 @@ WrittenModelInfo CreateWMI(StructureDefinition t)
exportName = t.Name.ToPascalCase();
}
- return new WrittenModelInfo()
- {
- FhirName = t.Name,
- CsName = $"{Namespace}.{exportName}",
- IsAbstract = t.Abstract == true,
- };
+ return new WrittenModelInfo(FhirName: t.Name, CsName: $"{Namespace}.{exportName}", IsAbstract: t.Abstract == true);
}
}
/// Information about a written value set.
- internal struct WrittenValueSetInfo
- {
- internal string ClassName;
- internal string ValueSetName;
- }
+ internal record WrittenValueSetInfo(string ClassName, string ValueSetName);
/// Information about the written element.
internal record WrittenElementInfo(
@@ -4061,16 +3839,9 @@ internal record WrittenElementInfo(
string FhirElementPath,
string PropertyName,
TypeReference PropertyType,
- string? PrimitiveHelperName)
- {
- //public string FhirElementName => FhirElementPath.Split('.').Last();
- }
+ string? PrimitiveHelperName,
+ bool Required = false);
/// Information about the written model.
- internal struct WrittenModelInfo
- {
- internal string FhirName;
- internal string CsName;
- internal bool IsAbstract;
- }
+ internal record WrittenModelInfo(string FhirName, string CsName, bool IsAbstract);
}
diff --git a/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/CSharpFirelyCommon.cs b/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/CSharpFirelyCommon.cs
index a3662d2c7..fc93f9863 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/CSharpFirelyCommon.cs
+++ b/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/CSharpFirelyCommon.cs
@@ -4,8 +4,12 @@
//
using System.ComponentModel;
+using System.Text;
using Hl7.Fhir.Model;
using Microsoft.Health.Fhir.CodeGen.FhirExtensions;
+using Microsoft.Health.Fhir.CodeGenCommon.Extensions;
+using Microsoft.Health.Fhir.CodeGenCommon.Packaging;
+using Microsoft.Health.Fhir.CodeGenCommon.Utils;
#if NETSTANDARD2_0
using Microsoft.Health.Fhir.CodeGenCommon.Polyfill;
@@ -17,7 +21,7 @@ public static class CSharpFirelyCommon
{
/// Dictionary mapping FHIR primitive types to language equivalents (see Template-Model.tt#1252).
- public static readonly Dictionary PrimitiveTypeMap = new Dictionary()
+ public static readonly Dictionary PrimitiveTypeMap = new()
{
{ "base64Binary", "byte[]" },
{ "boolean", "bool?" },
@@ -61,22 +65,6 @@ public static class CSharpFirelyCommon
{ "Resource", "DomainResource" },
};
- /// Primitive types that have a specific validation attribute on their Value property.
- public static readonly Dictionary PrimitiveValidationPatterns = new()
- {
- ["uri"] = "UriPattern",
- ["uuid"] = "UuidPattern",
- ["id"] = "IdPattern",
- ["date"] = "DatePattern",
- ["dateTime"] = "DateTimePattern",
- ["oid"] = "OidPattern",
- ["code"] = "CodePattern",
- ["time"] = "TimePattern",
- ["string"] = "StringPattern",
- ["markdown"] = "StringPattern",
- ["xhtml"] = "NarrativeXhtmlPattern"
- };
-
///
/// Determines the subset of code to generate.
///
@@ -251,4 +239,27 @@ public static int GetOrder(int relativeOrder)
{
return (relativeOrder * 10) + 10;
}
+
+ public static string BuildOpenAllowedTypesAttribute() => "[AllowedTypes(OpenChoice = true)]";
+
+ public static string BuildAllowedTypesAttribute(IEnumerable types, FhirReleases.FhirSequenceCodes? since)
+ {
+ StringBuilder sb = new();
+ sb.Append("[AllowedTypes(");
+
+ string typesList = string.Join(",",
+ types.Select(t => $"typeof({t.PropertyTypeString})"));
+
+ sb.Append(typesList);
+ if (since is not null)
+ sb.Append($", Since = FhirRelease.{since}");
+ sb.Append(")]");
+ return sb.ToString();
+ }
+}
+
+
+public static class StringHelpers
+{
+ public static string EnsurePeriod(this string s) => s.EndsWith('.') ? s : s + ".";
}
diff --git a/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/FirelyNetIG.cs b/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/FirelyNetIG.cs
index 1a1a64635..1fbf6c5f6 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/FirelyNetIG.cs
+++ b/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/FirelyNetIG.cs
@@ -724,7 +724,7 @@ private void ProcessAndWriteValueSets()
foreach ((string unversionedUrl, string[] versions) in _info.ValueSetVersions.OrderBy(kvp => kvp.Key))
{
// check for exclusions
- if (CSharpFirely2._exclusionSet.Contains(unversionedUrl))
+ if (CSharpFirely2.ExclusionSet.Contains(unversionedUrl))
{
continue;
}
@@ -808,7 +808,7 @@ private void ProcessAndWriteValueSet(
// Enums and their containing classes cannot have the same name,
// so we have to correct these here
- if (CSharpFirely2._enumNamesOverride.TryGetValue(vs.Url, out var replacementName))
+ if (CSharpFirely2.EnumNamesOverride.TryGetValue(vs.Url, out var replacementName))
{
nameSanitized = replacementName;
}
@@ -2159,9 +2159,9 @@ private void WriteProfile(StructureDefinition sd)
break;
//throw new Exception($"Found multiple discriminators for {id}");
}
-
+
if (discriminators.Length == 1)
- {
+ {
discriminator = discriminators[0];
bool isExtensionSlice = _findExtensionPathRegex.IsMatch(discriminator.Path);
@@ -3361,7 +3361,7 @@ private ExtensionData GetExtensionData(
remarks = (remarks == null ? string.Empty : remarks + "\n") +
$"Structure Definition Name: {cd.Structure.Name}";
}
-
+
string directive;
if (_info.TryGetPackageSource(cd.Structure, out string packageId, out string packageVersion))
{
@@ -3567,7 +3567,7 @@ private ExtensionData GetExtensionData(
Expression = "DataType",
},
ContextTarget = null,
- ContextElementInfo = new("", "", "", new ChoiceTypeReference(), null),
+ ContextElementInfo = new("", "", "", ComplexTypeReference.DataTypeReference, null),
//ContextElementInfo = new CSharpFirely2.WrittenElementInfo()
//{
// ElementType = "Hl7.Fhir.Model.DataType",
diff --git a/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/TypeReference.cs b/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/TypeReference.cs
index 19b7b2a09..cf8360a8b 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/TypeReference.cs
+++ b/src/Microsoft.Health.Fhir.CodeGen/Language/Firely/TypeReference.cs
@@ -1,5 +1,3 @@
-#nullable enable
-
using Microsoft.Health.Fhir.CodeGenCommon.Extensions;
using Microsoft.Health.Fhir.CodeGenCommon.Utils;
@@ -7,6 +5,19 @@ namespace Microsoft.Health.Fhir.CodeGen.Language.Firely;
public abstract record TypeReference(string Name)
{
+ public static TypeReference BuildFromFhirTypeName(string name, string? vsName=null, string? vsClass=null)
+ {
+ // Elements of type Code or Code have their own naming/types, so handle those separately.
+ if (name == "code" && vsName is not null)
+ return new CodedTypeReference(vsName, vsClass);
+
+ if (PrimitiveTypeReference.IsFhirPrimitiveType(name))
+ return PrimitiveTypeReference.GetTypeReference(name);
+
+ // Otherwise, this is a "normal" name for a complex type.
+ return new ComplexTypeReference(name, MapTypeName(name));
+ }
+
public abstract string PropertyTypeString { get; }
internal static string MapTypeName(string name)
@@ -43,27 +54,42 @@ public record PrimitiveTypeReference(string Name, string PocoTypeName, Type Conv
public static PrimitiveTypeReference ForTypeName(string name, Type propertyType) =>
new(name, MapTypeName(name), propertyType);
+ public static readonly PrimitiveTypeReference PrimitiveType = ForTypeName("PrimitiveType", typeof(object));
+ public static readonly PrimitiveTypeReference Boolean = ForTypeName("boolean", typeof(bool));
+ public static readonly PrimitiveTypeReference Base64Binary = ForTypeName("base64Binary", typeof(byte[]));
+ public static readonly PrimitiveTypeReference Canonical = ForTypeName("canonical", typeof(string));
+ public static readonly PrimitiveTypeReference Code = ForTypeName("code", typeof(string));
+ public static readonly PrimitiveTypeReference Date = ForTypeName("date", typeof(string));
+ public static readonly PrimitiveTypeReference DateTime = ForTypeName("dateTime", typeof(string));
+ public static readonly PrimitiveTypeReference Decimal = ForTypeName("decimal", typeof(decimal));
+ public static readonly PrimitiveTypeReference Id = ForTypeName("id", typeof(string));
+ public static readonly PrimitiveTypeReference Instant = ForTypeName("instant", typeof(DateTimeOffset));
+ public static readonly PrimitiveTypeReference Integer = ForTypeName("integer", typeof(int));
+ public static readonly PrimitiveTypeReference Integer64 = ForTypeName("integer64", typeof(long));
+ public static readonly PrimitiveTypeReference Oid = ForTypeName("oid", typeof(string));
+ public static readonly PrimitiveTypeReference PositiveInt = ForTypeName("positiveInt", typeof(int));
+ public static readonly PrimitiveTypeReference String = ForTypeName("string", typeof(string));
+ public static readonly PrimitiveTypeReference Time = ForTypeName("time", typeof(string));
+ public static readonly PrimitiveTypeReference UnsignedInt = ForTypeName("unsignedInt", typeof(int));
+ public static readonly PrimitiveTypeReference Uri = ForTypeName("uri", typeof(string));
+ public static readonly PrimitiveTypeReference Url = ForTypeName("url", typeof(string));
+ public static readonly PrimitiveTypeReference Xhtml = ForTypeName("xhtml", typeof(string));
+ public static readonly PrimitiveTypeReference Markdown = ForTypeName("markdown", typeof(string));
+
public static readonly IReadOnlyCollection PrimitiveList =
[
- ForTypeName("base64Binary", typeof(byte[])), ForTypeName("boolean", typeof(bool)),
- ForTypeName("canonical", typeof(string)), ForTypeName("code", typeof(string)),
- ForTypeName("date", typeof(string)), ForTypeName("dateTime", typeof(string)),
- ForTypeName("decimal", typeof(decimal)), ForTypeName("id", typeof(string)),
- ForTypeName("instant", typeof(DateTimeOffset)), ForTypeName("integer", typeof(int)),
- ForTypeName("integer64", typeof(long)), ForTypeName("oid", typeof(string)),
- ForTypeName("positiveInt", typeof(int)), ForTypeName("string", typeof(string)),
- ForTypeName("time", typeof(string)), ForTypeName("unsignedInt", typeof(int)),
- ForTypeName("uri", typeof(string)), ForTypeName("url", typeof(string)),
- ForTypeName("xhtml", typeof(string)), ForTypeName("markdown", typeof(string))
+ Boolean, Base64Binary, Canonical, Code, Date, DateTime, Decimal, Id,
+ Instant, Integer, Integer64, Oid, PositiveInt, String, Time, UnsignedInt,
+ Uri, Url, Xhtml, Markdown
];
- private static readonly Dictionary s_primitiveDictionary =
+ private static readonly Dictionary _primitiveDictionary =
PrimitiveList.ToDictionary(ptr => ptr.Name);
- public static bool IsFhirPrimitiveType(string name) => s_primitiveDictionary.ContainsKey(name);
+ public static bool IsFhirPrimitiveType(string name) => _primitiveDictionary.ContainsKey(name);
public static PrimitiveTypeReference GetTypeReference(string name) =>
- s_primitiveDictionary.TryGetValue(name, out var tr)
+ _primitiveDictionary.TryGetValue(name, out var tr)
? tr
: throw new InvalidOperationException($"Unknown FHIR primitive {name}");
@@ -83,10 +109,12 @@ public record CqlTypeReference(string Name, Type PropertyType) : TypeReference(N
public record ComplexTypeReference(string Name, string PocoTypeName) : TypeReference(Name)
{
+ public ComplexTypeReference(string name) : this(name, name) { }
+
public override string PropertyTypeString => $"Hl7.Fhir.Model.{PocoTypeName}";
-}
-public record ChoiceTypeReference() : ComplexTypeReference("DataType", "DataType");
+ public static readonly ComplexTypeReference DataTypeReference = new("DataType");
+}
public record CodedTypeReference(string EnumName, string? EnumClassName)
: PrimitiveTypeReference("code", EnumName, typeof(Enum))
diff --git a/src/Microsoft.Health.Fhir.CodeGen/Loader/PackageLoader.cs b/src/Microsoft.Health.Fhir.CodeGen/Loader/PackageLoader.cs
index 2ee8e348e..75126bda7 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/Loader/PackageLoader.cs
+++ b/src/Microsoft.Health.Fhir.CodeGen/Loader/PackageLoader.cs
@@ -126,7 +126,7 @@ public PackageLoader(ConfigRoot? config = null, LoaderOptions? opts = null)
_defaultFhirVersion = FhirReleases.FhirVersionToSequence(_rootConfiguration.FhirVersion);
- _jsonOptions = opts.FhirJsonOptions;
+ _jsonOptions = opts.FhirJsonOptions.UsingMode(DeserializerModes.Ostrich);
_jsonParser = new(opts.FhirJsonSettings);
#if !DISABLE_XML
diff --git a/src/Microsoft.Health.Fhir.CodeGen/Microsoft.Health.Fhir.CodeGen.csproj b/src/Microsoft.Health.Fhir.CodeGen/Microsoft.Health.Fhir.CodeGen.csproj
index cef55dd98..f5018a55c 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/Microsoft.Health.Fhir.CodeGen.csproj
+++ b/src/Microsoft.Health.Fhir.CodeGen/Microsoft.Health.Fhir.CodeGen.csproj
@@ -33,7 +33,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
@@ -51,5 +51,5 @@
-
+
diff --git a/src/Microsoft.Health.Fhir.CodeGen/Models/ComponentDefinition.cs b/src/Microsoft.Health.Fhir.CodeGen/Models/ComponentDefinition.cs
index c477ed497..932b79295 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/Models/ComponentDefinition.cs
+++ b/src/Microsoft.Health.Fhir.CodeGen/Models/ComponentDefinition.cs
@@ -60,7 +60,7 @@ public ComponentDefinition(StructureDefinition sd)
/// Cg explicit name.
/// A string.
- public string cgExplicitName() => Element?.cgExplicitName() ?? string.Empty;
+ public string? cgExplicitName() => Element.cgExplicitName();
/// Gets the code generation name.
/// Note: Firely generation uses this version.
diff --git a/src/Microsoft.Health.Fhir.CodeGen/Models/DefinitionCollection.cs b/src/Microsoft.Health.Fhir.CodeGen/Models/DefinitionCollection.cs
index c6baa3412..ce2c877b2 100644
--- a/src/Microsoft.Health.Fhir.CodeGen/Models/DefinitionCollection.cs
+++ b/src/Microsoft.Health.Fhir.CodeGen/Models/DefinitionCollection.cs
@@ -854,8 +854,14 @@ private void ProcessParameters(OperationDefinition op)
fo = outFieldOrder.Count + 1;
outFieldOrder.Add(pc.Name, fo);
}
- else
+ else
{
+ if (inFieldOrder.ContainsKey(pc.Name))
+ {
+ Console.WriteLine($"Operation: {op.Id} ({op.Url}) defines the parameter {pc.Name} more than once!!!");
+ continue;
+ }
+
fo = inFieldOrder.Count + 1;
inFieldOrder.Add(pc.Name, fo);
}
diff --git a/src/Microsoft.Health.Fhir.CodeGenCommon/Extensions/FhirNameConventionExtensions.cs b/src/Microsoft.Health.Fhir.CodeGenCommon/Extensions/FhirNameConventionExtensions.cs
index 893967a04..54b7c976b 100644
--- a/src/Microsoft.Health.Fhir.CodeGenCommon/Extensions/FhirNameConventionExtensions.cs
+++ b/src/Microsoft.Health.Fhir.CodeGenCommon/Extensions/FhirNameConventionExtensions.cs
@@ -95,7 +95,7 @@ public static string ToPascalCase(
return string.Join(joinDelimiter, word.Split(delimitersToRemove, _wordSplitOptions).Select(w => w.ToPascalCase(false)));
}
- return string.Concat(word.Substring(0, 1).ToUpperInvariant(), word.Substring(1));
+ return string.Concat(word[..1].ToUpperInvariant(), word[1..]);
}
/// An extension method that converts an array of words each to PascalCase.
@@ -163,10 +163,10 @@ public static string ToCamelCase(
{
// converting to pascal and changing the initial letter is faster than accumulating here
string pc = word.ToPascalCase(removeDelimiters, joinDelimiter, delimitersToRemove);
- return string.Concat(pc.Substring(0, 1).ToLowerInvariant(), pc.Substring(1));
+ return string.Concat(pc[..1].ToLowerInvariant(), pc[1..]);
}
- return string.Concat(word.Substring(0, 1).ToLowerInvariant(), word.Substring(1));
+ return string.Concat(word[..1].ToLowerInvariant(), word[1..]);
}
/// An extension method that converts an array of words each to camelCase.
diff --git a/src/Microsoft.Health.Fhir.CodeGenCommon/Microsoft.Health.Fhir.CodeGenCommon.csproj b/src/Microsoft.Health.Fhir.CodeGenCommon/Microsoft.Health.Fhir.CodeGenCommon.csproj
index 2ed73f60f..7fbee7921 100644
--- a/src/Microsoft.Health.Fhir.CodeGenCommon/Microsoft.Health.Fhir.CodeGenCommon.csproj
+++ b/src/Microsoft.Health.Fhir.CodeGenCommon/Microsoft.Health.Fhir.CodeGenCommon.csproj
@@ -10,7 +10,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/src/Microsoft.Health.Fhir.CodeGenCommon/Packaging/FhirReleases.cs b/src/Microsoft.Health.Fhir.CodeGenCommon/Packaging/FhirReleases.cs
index b6617fc12..f2ee611a3 100644
--- a/src/Microsoft.Health.Fhir.CodeGenCommon/Packaging/FhirReleases.cs
+++ b/src/Microsoft.Health.Fhir.CodeGenCommon/Packaging/FhirReleases.cs
@@ -37,7 +37,7 @@ public enum FhirSequenceCodes : int
/// FHIR R5.
R5 = 5,
- /// FHIR R5.
+ /// FHIR R6.
R6 = 6,
}
diff --git a/src/Microsoft.Health.Fhir.CrossVersion/Microsoft.Health.Fhir.CrossVersion.csproj b/src/Microsoft.Health.Fhir.CrossVersion/Microsoft.Health.Fhir.CrossVersion.csproj
index 7c434ab8b..735bed944 100644
--- a/src/Microsoft.Health.Fhir.CrossVersion/Microsoft.Health.Fhir.CrossVersion.csproj
+++ b/src/Microsoft.Health.Fhir.CrossVersion/Microsoft.Health.Fhir.CrossVersion.csproj
@@ -19,7 +19,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/src/Microsoft.Health.Fhir.MappingLanguage/Microsoft.Health.Fhir.MappingLanguage.csproj b/src/Microsoft.Health.Fhir.MappingLanguage/Microsoft.Health.Fhir.MappingLanguage.csproj
index 594c46597..68c146ff8 100644
--- a/src/Microsoft.Health.Fhir.MappingLanguage/Microsoft.Health.Fhir.MappingLanguage.csproj
+++ b/src/Microsoft.Health.Fhir.MappingLanguage/Microsoft.Health.Fhir.MappingLanguage.csproj
@@ -16,7 +16,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/src/fhir-codegen/Properties/launchSettings.json b/src/fhir-codegen/Properties/launchSettings.json
index 0ad39befd..baded558a 100644
--- a/src/fhir-codegen/Properties/launchSettings.json
+++ b/src/fhir-codegen/Properties/launchSettings.json
@@ -7,157 +7,157 @@
},
"Firely 5.x Base": {
"commandName": "Project",
- "commandLineArgs": "generate CSharpFirely2 --output-path ..\\..\\..\\firely-net-sdk\\src\\Hl7.Fhir.Base\\Model -p hl7.fhir.r5.core#5.0.0 -p hl7.fhir.r5.expansions#5.0.0 --subset base",
+ "commandLineArgs": "generate CSharpFirely2 --output-path ../../../firely-net-sdk/src/Hl7.Fhir.Base/Model -p hl7.fhir.r5.core#5.0.0 -p hl7.fhir.r5.expansions#5.0.0 --subset base",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely 5.x Conformance": {
"commandName": "Project",
- "commandLineArgs": "generate CSharpFirely2 --output-path ..\\..\\..\\firely-net-sdk\\src\\Hl7.Fhir.Conformance\\Model -p hl7.fhir.r5.core#5.0.0 -p hl7.fhir.r5.expansions#5.0.0 --subset conformance",
+ "commandLineArgs": "generate CSharpFirely2 --output-path ../../../firely-net-sdk/src/Hl7.Fhir.Conformance/Model -p hl7.fhir.r5.core#5.0.0 -p hl7.fhir.r5.expansions#5.0.0 --subset conformance",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely 5.x STU3": {
"commandName": "Project",
- "commandLineArgs": "generate CSharpFirely2 --output-path ..\\..\\..\\firely-net-sdk\\src\\Hl7.Fhir.STU3\\Model -p hl7.fhir.r3.core#3.0.2 -p hl7.fhir.r3.expansions#3.0.2 --subset satellite",
+ "commandLineArgs": "generate CSharpFirely2 --output-path ../../../firely-net-sdk/src/Hl7.Fhir.STU3/Model -p hl7.fhir.r3.core#3.0.2 -p hl7.fhir.r3.expansions#3.0.2 --subset satellite",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely 5.x R4": {
"commandName": "Project",
- "commandLineArgs": "generate CSharpFirely2 --output-path ..\\..\\..\\firely-net-sdk\\src\\Hl7.Fhir.R4\\Model -p hl7.fhir.r4.core#4.0.1 -p hl7.fhir.r4.expansions#4.0.1 --subset satellite --cql-model Fhir401",
+ "commandLineArgs": "generate CSharpFirely2 --output-path ../../../firely-net-sdk/src/Hl7.Fhir.R4/Model -p hl7.fhir.r4.core#4.0.1 -p hl7.fhir.r4.expansions#4.0.1 --subset satellite --cql-model Fhir401",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely 5.x R4B": {
"commandName": "Project",
- "commandLineArgs": "generate CSharpFirely2 --output-path ..\\..\\..\\firely-net-sdk\\src\\Hl7.Fhir.R4B\\Model -p hl7.fhir.r4b.core#4.3.0 -p hl7.fhir.r4b.expansions#4.3.0 --subset satellite",
+ "commandLineArgs": "generate CSharpFirely2 --output-path ../../../firely-net-sdk/src/Hl7.Fhir.R4B/Model -p hl7.fhir.r4b.core#4.3.0 -p hl7.fhir.r4b.expansions#4.3.0 --subset satellite",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely 5.x R5": {
"commandName": "Project",
- "commandLineArgs": "generate CSharpFirely2 --output-path ..\\..\\..\\firely-net-sdk\\src\\Hl7.Fhir.R5\\Model -p hl7.fhir.r5.core#5.0.0 -p hl7.fhir.r5.expansions#5.0.0 --subset satellite",
+ "commandLineArgs": "generate CSharpFirely2 --output-path ../../../firely-net-sdk/src/Hl7.Fhir.R5/Model -p hl7.fhir.r5.core#5.0.0 -p hl7.fhir.r5.expansions#5.0.0 --subset satellite",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely 5.x R6": {
"commandName": "Project",
- "commandLineArgs": "generate CSharpFirely2 --output-path ..\\..\\..\\firely-net-sdk\\src\\Hl7.Fhir.R6\\Model -p hl7.fhir.r6.core#6.0.0-ballot2 -p hl7.fhir.r6.expansions#6.0.0-ballot2 --subset satellite",
+ "commandLineArgs": "generate CSharpFirely2 --output-path ../../../firely-net-sdk/src/Hl7.Fhir.R6/Model -p hl7.fhir.r6.core#6.0.0-ballot3 -p hl7.fhir.r6.expansions#6.0.0-ballot3 --subset satellite",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely IG Backport": {
"commandName": "Project",
- "commandLineArgs": "generate FirelyNetIg --output-path ..\\..\\temp\\firely-ig -p hl7.fhir.uv.subscriptions-backport#1.1.0 --fhir-version 4.0.1 --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate FirelyNetIg --output-path ../../temp/firely-ig -p hl7.fhir.uv.subscriptions-backport#1.1.0 --fhir-version 4.0.1 --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely IG US Core": {
"commandName": "Project",
- "commandLineArgs": "generate FirelyNetIg --output-path ..\\..\\temp\\firely-ig -p hl7.fhir.us.core#6.1.0 --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate FirelyNetIg --output-path ../../temp/firely-ig -p hl7.fhir.us.core#6.1.0 --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely IG Extensions Pack": {
"commandName": "Project",
- "commandLineArgs": "generate FirelyNetIg --output-path ..\\..\\temp\\firely-ig -p hl7.fhir.uv.extensions.r4#latest --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate FirelyNetIg --output-path ../../temp/firely-ig -p hl7.fhir.uv.extensions.r4#latest --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely IG NDH": {
"commandName": "Project",
- "commandLineArgs": "generate FirelyNetIg --output-path ..\\..\\temp\\firely-ig -p hl7.fhir.us.ndh#1.0.0-ballot --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate FirelyNetIg --output-path ../../temp/firely-ig -p hl7.fhir.us.ndh#1.0.0-ballot --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely IG SDC": {
"commandName": "Project",
- "commandLineArgs": "generate FirelyNetIg --output-path ..\\..\\temp\\firely-ig -p hl7.fhir.uv.sdc#3.0.0 --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate FirelyNetIg --output-path ../../temp/firely-ig -p hl7.fhir.uv.sdc#3.0.0 --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Firely OpenApi": {
"commandName": "Project",
- "commandLineArgs": "generate OpenApi --fhir-server-url https://secure.server.fire.ly/ -p hl7.fhir.r4.core#4.0.1 --output-path ..\\..\\generated\\FS-OpenApi-R4 --include-experimental --schema-level names --metadata true --multi-file true --single-responses false --resolve-server-canonicals false --resolve-external-canonicals false --basic-scopes-only true",
+ "commandLineArgs": "generate OpenApi --fhir-server-url https://secure.server.fire.ly/ -p hl7.fhir.r4.core#4.0.1 --output-path ../../generated/FS-OpenApi-R4 --include-experimental --schema-level names --metadata true --multi-file true --single-responses false --resolve-server-canonicals false --resolve-external-canonicals false --basic-scopes-only true",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Info R2": {
"commandName": "Project",
- "commandLineArgs": "generate Info --output-path ..\\..\\generated --output-filename Info_R2.txt -p hl7.fhir.r2.core@1.0.2 -p hl7.fhir.r2.expansions#1.0.2",
+ "commandLineArgs": "generate Info --output-path ../../generated --output-filename Info_R2.txt -p hl7.fhir.r2.core@1.0.2 -p hl7.fhir.r2.expansions#1.0.2",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Info R3": {
"commandName": "Project",
- "commandLineArgs": "generate Info --output-path ..\\..\\generated --output-filename Info_R3.txt -p hl7.fhir.r3.core@3.0.2 -p hl7.fhir.r3.expansions#3.0.2",
+ "commandLineArgs": "generate Info --output-path ../../generated --output-filename Info_R3.txt -p hl7.fhir.r3.core@3.0.2 -p hl7.fhir.r3.expansions#3.0.2",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Info R4": {
"commandName": "Project",
- "commandLineArgs": "generate Info --output-path ..\\..\\generated --output-filename Info_R4.txt -p hl7.fhir.r4.core@latest -p hl7.fhir.r4.expansions#4.0.1",
+ "commandLineArgs": "generate Info --output-path ../../generated --output-filename Info_R4.txt -p hl7.fhir.r4.core@latest -p hl7.fhir.r4.expansions#4.0.1",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Info R4B": {
"commandName": "Project",
- "commandLineArgs": "generate Info --output-path ..\\..\\generated --output-filename Info_R4B.txt -p hl7.fhir.r4b.core@4.3.0 -p hl7.fhir.r4b.expansions#4.3.0",
+ "commandLineArgs": "generate Info --output-path ../../generated --output-filename Info_R4B.txt -p hl7.fhir.r4b.core@4.3.0 -p hl7.fhir.r4b.expansions#4.3.0",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Info R5": {
"commandName": "Project",
- "commandLineArgs": "generate Info --output-path ..\\..\\generated --output-filename Info_R5.txt -p hl7.fhir.r5.core@5.0.0 -p hl7.fhir.r5.expansions#5.0.0",
+ "commandLineArgs": "generate Info --output-path ../../generated --output-filename Info_R5.txt -p hl7.fhir.r5.core@5.0.0 -p hl7.fhir.r5.expansions#5.0.0",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Shorthand IG US Core": {
"commandName": "Project",
- "commandLineArgs": "generate ShorthandIg --output-path ..\\..\\temp\\fsh-ig -p hl7.fhir.us.core#6.1.0 --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate ShorthandIg --output-path ../../temp/fsh-ig -p hl7.fhir.us.core#6.1.0 --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Shorthand IG Extensions Pack": {
"commandName": "Project",
- "commandLineArgs": "generate ShorthandIg --output-path ..\\..\\temp\\fsh-ig -p hl7.fhir.uv.extensions.r4#latest --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate ShorthandIg --output-path ../../temp/fsh-ig -p hl7.fhir.uv.extensions.r4#latest --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Shorthand IG NDH": {
"commandName": "Project",
- "commandLineArgs": "generate ShorthandIg --output-path ..\\..\\temp\\fsh-ig -p hl7.fhir.us.ndh#1.0.0-ballot --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate ShorthandIg --output-path ../../temp/fsh-ig -p hl7.fhir.us.ndh#1.0.0-ballot --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Shorthand IG SDC": {
"commandName": "Project",
- "commandLineArgs": "generate ShorthandIg --output-path ..\\..\\temp\\fsh-ig -p hl7.fhir.uv.sdc#3.0.0 --auto-load-expansions --resolve-dependencies true --include-experimental",
+ "commandLineArgs": "generate ShorthandIg --output-path ../../temp/fsh-ig -p hl7.fhir.uv.sdc#3.0.0 --auto-load-expansions --resolve-dependencies true --include-experimental",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"TypeScript R2": {
"commandName": "Project",
- "commandLineArgs": "generate TypeScript --output-path ..\\..\\generated --output-filename TypeScript_R2.ts -p hl7.fhir.r2.core#1.0.2 -p hl7.fhir.r2.expansions#1.0.2",
+ "commandLineArgs": "generate TypeScript --output-path ../../generated --output-filename TypeScript_R2.ts -p hl7.fhir.r2.core#1.0.2 -p hl7.fhir.r2.expansions#1.0.2",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"TypeScript R3": {
"commandName": "Project",
- "commandLineArgs": "generate TypeScript --output-path ..\\..\\generated --output-filename TypeScript_R3.ts -p hl7.fhir.r3.core#3.0.2 -p hl7.fhir.r3.expansions#3.0.2",
+ "commandLineArgs": "generate TypeScript --output-path ../../generated --output-filename TypeScript_R3.ts -p hl7.fhir.r3.core#3.0.2 -p hl7.fhir.r3.expansions#3.0.2",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"TypeScript R4": {
"commandName": "Project",
- "commandLineArgs": "generate TypeScript --output-path ..\\..\\generated --output-filename TypeScript_R4.ts -p hl7.fhir.r4.core#4.0.1 -p hl7.fhir.r4.expansions#4.0.1",
+ "commandLineArgs": "generate TypeScript --output-path ../../generated --output-filename TypeScript_R4.ts -p hl7.fhir.r4.core#4.0.1 -p hl7.fhir.r4.expansions#4.0.1",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"TypeScript R4B": {
"commandName": "Project",
- "commandLineArgs": "generate TypeScript --output-path ..\\..\\generated --output-filename TypeScript_R4B.ts -p hl7.fhir.r4b.core#4.3.0 -p hl7.fhir.r4b.expansions#4.3.0",
+ "commandLineArgs": "generate TypeScript --output-path ../../generated --output-filename TypeScript_R4B.ts -p hl7.fhir.r4b.core#4.3.0 -p hl7.fhir.r4b.expansions#4.3.0",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"TypeScript R5": {
"commandName": "Project",
- "commandLineArgs": "generate TypeScript --output-path ..\\..\\generated --output-filename TypeScript_R5.ts -p hl7.fhir.r5.core#5.0.0 -p hl7.fhir.r5.expansions#5.0.0",
+ "commandLineArgs": "generate TypeScript --output-path ../../generated --output-filename TypeScript_R5.ts -p hl7.fhir.r5.core#5.0.0 -p hl7.fhir.r5.expansions#5.0.0",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Ruby R4": {
"commandName": "Project",
- "commandLineArgs": "generate Ruby --output-path ..\\..\\generated\\Ruby_r4 -p hl7.fhir.r4.core#4.0.1 -p hl7.fhir.r4.expansions#4.0.1",
+ "commandLineArgs": "generate Ruby --output-path ../../generated/Ruby_r4 -p hl7.fhir.r4.core#4.0.1 -p hl7.fhir.r4.expansions#4.0.1",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Compare x-y": {
"commandName": "Project",
- "commandLineArgs": "compare -c hl7.fhir.r4.core#4.0.1 -p hl7.fhir.r5.core#5.0.0 --auto-load-expansions --resolve-dependencies true --map-source-path ..\\..\\..\\fhir-cross-version --map-destination-path ..\\..\\..\\fhir-cross-version-source --map-save-style Source",
+ "commandLineArgs": "compare -c hl7.fhir.r4.core#4.0.1 -p hl7.fhir.r5.core#5.0.0 --auto-load-expansions --resolve-dependencies true --map-source-path ../../../fhir-cross-version --map-destination-path ../../../fhir-cross-version-source --map-save-style Source",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Compare 43-50": {
"commandName": "Project",
- "commandLineArgs": "compare -p hl7.fhir.r4b.core#4.3.0 -c hl7.fhir.r5.core#5.0.0 --auto-load-expansions --resolve-dependencies true --map-source-path ..\\..\\..\\fhir-cross-version --map-destination-path ..\\..\\..\\fhir-cross-version-source --map-save-style Source",
+ "commandLineArgs": "compare -p hl7.fhir.r4b.core#4.3.0 -c hl7.fhir.r5.core#5.0.0 --auto-load-expansions --resolve-dependencies true --map-source-path ../../../fhir-cross-version --map-destination-path ../../../fhir-cross-version-source --map-save-style Source",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Compare 50-43": {
"commandName": "Project",
- "commandLineArgs": "compare -p hl7.fhir.r5.core#5.0.0 -c hl7.fhir.r4b.core#4.3.0 --auto-load-expansions --resolve-dependencies true --map-source-path ..\\..\\..\\fhir-cross-version --map-destination-path ..\\..\\..\\fhir-cross-version-source --map-save-style Source",
+ "commandLineArgs": "compare -p hl7.fhir.r5.core#5.0.0 -c hl7.fhir.r4b.core#4.3.0 --auto-load-expansions --resolve-dependencies true --map-source-path ../../../fhir-cross-version --map-destination-path ../../../fhir-cross-version-source --map-save-style Source",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"Gui": {
@@ -167,7 +167,7 @@
},
"Cross Version Review": {
"commandName": "Project",
- "commandLineArgs": "cross-version --output-path ..\\..\\temp\\cross-version",
+ "commandLineArgs": "cross-version --output-path ../../temp/cross-version",
"workingDirectory": "$(MSBuildProjectDirectory)"
},
"http": {
diff --git a/src/fhir-codegen/fhir-codegen.csproj b/src/fhir-codegen/fhir-codegen.csproj
index 270a5021f..574351448 100644
--- a/src/fhir-codegen/fhir-codegen.csproj
+++ b/src/fhir-codegen/fhir-codegen.csproj
@@ -37,7 +37,7 @@
-
+