bool ILanguangeExporter.AppendCodes( ref StringBuilder sb, FhirProperty property, string parentName ) { // **** put a comment **** sb.Append($"/**\n * Code Values for the {parentName}.{property.Name} field\n */\n"); // **** open our enum *** sb.Append($"export enum {parentName}{property.NameCapitalized}Codes {{\n"); // **** start adding values **** foreach (string code in property.CodeValues) { FhirTypeManager.SanitizeForCode(code, _reservedWordsSet, out string name, out string value); sb.Append($"\t{name.ToUpper()} = \"{value}\",\n"); } // **** close our enum *** sb.Append("}\n"); return(true); }
bool ILanguangeExporter.AppendCodes( ref StringBuilder sb, FhirProperty property, string parentName ) { // **** put a comment **** sb.Append($"\t///<summary>Code Values for the {parentName}.{property.Name} field</summary>\n"); // **** open our enum *** sb.Append($"\tpublic sealed class {parentName}{property.NameCapitalized}Codes {{\n"); // **** start adding values **** foreach (string code in property.CodeValues) { FhirTypeManager.SanitizeForCode(code, _reservedWordsSet, out string name, out string value); sb.Append($"\t\tpublic const string {name.ToUpper()} = \"{value}\";\n"); } // **** close our enum *** sb.Append("\t}\n"); return(true); }
///------------------------------------------------------------------------------------------------- /// <summary>Main entry-point for this application.</summary> /// /// <remarks>Gino Canessa, 7/12/2019.</remarks> /// /// <param name="args">An array of command-line argument strings.</param> ///------------------------------------------------------------------------------------------------- static void Main(string[] args) { // **** start timing **** Stopwatch timingWatch = Stopwatch.StartNew(); // **** initialize our manager **** FhirTypeManager.Init(); // **** process based on command line arguments **** Parser.Default.ParseArguments <Options>(args) .WithParsed <Options>(options => { ProcessFhirDirectory( options ); }) .WithNotParsed(errors => { Console.WriteLine("Invalid arguments"); }); // **** done **** long elapsedMs = timingWatch.ElapsedMilliseconds; Console.WriteLine($"Finished in: {elapsedMs / 1000.0} s"); }
///------------------------------------------------------------------------------------------------- /// <summary>Process the structure primitive type.</summary> /// /// <remarks>Gino Canessa, 8/12/2019.</remarks> /// /// <param name="sd"> The SD.</param> /// <param name="filename">Filename of the file.</param> ///------------------------------------------------------------------------------------------------- static void ProcessStructurePrimitiveType(fhir.StructureDefinition sd, string filename) { //Console.WriteLine($"Primitive: {sd.Name}:{GetJsonTypeFromStructure(sd)} - {filename}"); // **** add our type **** FhirTypeManager.ProcessSpreadsheetType(sd.Name, GetPrimitiveJsonTypeFromStructure(sd), sd.Description, true, filename); }
///------------------------------------------------------------------------------------------------- /// <summary>Process the data elements table.</summary> /// /// <remarks>Gino Canessa, 7/8/2019.</remarks> /// /// <param name="dt"> The dt.</param> /// <param name="isPrimitive"> True if is primitive, false if not.</param> /// <param name="sourceFilename"> Filename of the source file.</param> /// <param name="firstElementName">[out] Name of the first element.</param> ///------------------------------------------------------------------------------------------------- static void ProcessDataElementsTable( DataTable dt, bool isPrimitive, string sourceFilename, out string firstElementName ) { // **** grab the indexes of the columns we care about **** int ordinalElement = dt.Columns["Element"].Ordinal; int ordinalCardinality = dt.Columns["Card."].Ordinal; int ordinalType = dt.Columns["Type"].Ordinal; int ordinalComment = dt.Columns["Definition"].Ordinal; int ordinalShortName = dt.Columns["Short Name"].Ordinal; // **** check for no rows **** if ((dt.Rows == null) || (dt.Rows.Count == 0)) { firstElementName = ""; return; } // **** grab the element name from the first row **** firstElementName = dt.Rows[0][ordinalElement].ToString(); // **** iterate over the items in this table **** foreach (DataRow row in dt.Rows) { // **** grab our data **** string element = row[ordinalElement].ToString(); string cardinality = row[ordinalCardinality].ToString(); string baseType = row[ordinalType].ToString(); string comment = row[ordinalComment].ToString(); // **** check for no comment (use short name) **** if (string.IsNullOrEmpty(comment)) { comment = row[ordinalShortName].ToString(); } // **** skip empty rows **** if (string.IsNullOrEmpty(element)) { continue; } // **** process this field **** FhirTypeManager.ProcessSpreadsheetDataElement(element, baseType, comment, cardinality, isPrimitive, sourceFilename); } }
bool ILanguangeExporter.AppendValueSetCodeConcept( ref StringBuilder sb, string sanitizedValueSetName, string sanitizedCodeName, fhir.CodeSystemConcept concept, string systemUrl ) { string comment; // **** start with a comment **** if (!string.IsNullOrEmpty(concept.Definition)) { comment = FhirTypeManager.SanitizeComment(concept.Definition, _lineComment, _indentChar, 2); } else if (!string.IsNullOrEmpty(concept.Display)) { comment = FhirTypeManager.SanitizeComment(concept.Display, _lineComment, _indentChar, 2); } else { comment = $"Value for '{concept.Code}'</summary>\n"; } // **** coding is exported inline (before internal and external interfaces) **** sb.Append($"const {sanitizedValueSetName}_{sanitizedCodeName}: Coding = {{\n"); sb.Append($"\t\tcode: \"{concept.Code}\",\n"); if (!string.IsNullOrEmpty(concept.Display)) { sb.Append($"\t\tdisplay: \"{concept.Display.Replace("\"", "\\\"")}\",\n"); } sb.Append($"\t\tsystem: \"{systemUrl}\"\n"); sb.Append($"\t}};\n"); // **** add this code to our interface **** //_valueSetInterfaceSB.Append($"\t/**\n\t * {comment}\n\t */\n"); //_valueSetInterfaceSB.Append($"\t{sanitizedCodeName}: Coding,\n"); // **** add this code to our export **** _valueSetExportSB.Append($"\t/**\n\t * {comment}\n\t */\n"); _valueSetExportSB.Append($"\t{sanitizedCodeName}: {sanitizedValueSetName}_{sanitizedCodeName},\n"); return(true); }
bool ILanguangeExporter.AppendFhirProperty( ref StringBuilder sb, FhirProperty property, string typeName, bool useLowerCaseName ) { // **** always use lower-case start **** string name = FhirTypeManager.SanitizeForProperty(property.Name, _reservedWordsSet); string comment = FhirTypeManager.SanitizeComment(property.Comment, _lineComment, _indentChar, 2); string optionalFlagString = (_flagOptionals && property.IsOptional) ? "?" : ""; // **** **** if (property.IsArray) { sb.Append( $"\t/**\n" + $"\t * {comment}\n" + //$"\t * Cardinality: {property.Cardinality}\n" + $"\t */\n" + $"\t{name}{optionalFlagString}: {typeName}[];\n" + $"\t/**\n" + $"\t * May contain extended information for property: '{name}'\n" + $"\t */\n" + $"\t_{name}?: Element[];\n" ); return(true); } sb.Append( $"\t/**\n" + $"\t * {comment}\n" + //$"\t * Cardinality: {property.Cardinality}\n" + $"\t */\n" + $"\t{name}{optionalFlagString}: {typeName};\n" + $"\t/**\n" + $"\t * May contain extended information for property: '{name}'\n" + $"\t */\n" + $"\t_{name}?: Element;\n" ); return(true); }
///------------------------------------------------------------------------------------------------- /// <summary>Writes a C#.</summary> /// /// <remarks>Gino Canessa, 8/12/2019.</remarks> /// /// <param name="options">Options for controlling the operation.</param> ///------------------------------------------------------------------------------------------------- static void WriteCSharp(Options options, int style) { LanguageCSharp lang = new LanguageCSharp(); // **** set the style **** lang.LanguageStyle = style; // **** enable/disable polymorphic deserialization **** lang.PolymorphicDeserialization = options.LanguageCSharpPolymorphic; // **** for our filename **** string filename; // **** check for having an extension **** if (Path.HasExtension(options.OutputFile)) { filename = options.OutputFile; } else { filename = $"{options.OutputFile}{lang.GetFilenamePartForStyle}.{((ILanguangeExporter)lang).SourceFileExtension}"; } // **** **** Console.WriteLine($"Writing C# ({lang.GetStyleName}) file: {filename}"); // **** start our file **** using (StreamWriter writer = new StreamWriter(filename)) { // **** output our data **** FhirTypeManager.OutputForLang( writer, lang, options.TypesToOutput, options.OutputNamespace, options.ExcludeCodes ); writer.Flush(); } }
///------------------------------------------------------------------------------------------------- /// <summary>Process the XML spreadsheets in a directory, as described by options.</summary> /// /// <remarks>Gino Canessa, 12/5/2019.</remarks> /// /// <param name="options">Options for controlling the operation.</param> /// /// <returns>True if it succeeds, false if it fails.</returns> ///------------------------------------------------------------------------------------------------- static bool ProcessXmlSpreadsheetsFor(Options options) { string dir = Path.Combine(options.FhirDirectory, "source"); // **** check for this directory existing **** if (!Directory.Exists(dir)) { Console.WriteLine("Source directory not found! Skipping XML Spreadsheets"); return(false); } // **** build our list of types to include **** string[] types = options.TypesForXmlSpreadsheets.Split('|'); // **** traverse the types we want **** foreach (string typeName in types) { // **** build the filename we expect **** string filename = Path.Combine(options.FhirDirectory, "source", typeName, $"{typeName}-spreadsheet.xml"); // **** check for this file **** if (!File.Exists(filename)) { Console.WriteLine($"Could not find {filename}, will not process XML spreadsheet!"); continue; } // **** remove this type (and subtypes) from the current list **** FhirTypeManager.RemoveType(typeName); // **** process this file **** ProcessResourceXmlFile(filename, false); } // **** ok **** return(true); }
bool ILanguangeExporter.AppendValueSetOpen(ref StringBuilder sb, string sanitizedAlias, string sanitizedName, fhir.ValueSet valueSet) { // **** start with a comment **** if (!string.IsNullOrEmpty(valueSet.Description)) { sb.Append($"\t///<summary>{FhirTypeManager.SanitizeComment(valueSet.Description, _lineComment, _indentChar, 1)}</summary>\n"); } else { sb.Append($"\t///<summary>Expanded ValueSet from {valueSet.Url}</summary>\n"); } // **** open our value set **** sb.Append($"\tpublic abstract class {sanitizedName}\n\t{{\n"); return(true); }
///------------------------------------------------------------------------------------------------- /// <summary>Process the primitive table.</summary> /// /// <remarks>Gino Canessa, 7/5/2019.</remarks> /// /// <param name="dt">The dt.</param> ///------------------------------------------------------------------------------------------------- static void ProcessPrimitiveTable(DataTable dt, string sourceFilename) { // **** grab the indexes of the columns we care about **** int ordinalName = dt.Columns["Data Type"].Ordinal; int ordinalType = dt.Columns["Json"].Ordinal; int ordinalComment = dt.Columns["Definition"].Ordinal; // **** iterate over the items in this table **** foreach (DataRow row in dt.Rows) { // **** grab our data **** string name = row[ordinalName].ToString(); string baseType = row[ordinalType].ToString(); string comment = row[ordinalComment].ToString(); FhirTypeManager.ProcessSpreadsheetType(name, baseType, comment, true, sourceFilename); } }
///------------------------------------------------------------------------------------------------- /// <summary>Process the restrictions table.</summary> /// /// <remarks>Gino Canessa, 7/10/2019.</remarks> /// /// <param name="dt"> The dt.</param> /// <param name="elementBaseName">Name of the element base.</param> /// <param name="isPrimitive"> True if is primitive, false if not.</param> /// <param name="sourceFilename"> Filename of the source file.</param> ///------------------------------------------------------------------------------------------------- static void ProcessRestrictionsTable( DataTable dt, string elementBaseName, bool isPrimitive, string sourceFilename ) { // **** grab the indexes of the columns we care about **** int ordinalName = dt.Columns["Name"].Ordinal; int ordinalComment = dt.Columns["Definition"].Ordinal; // **** iterate over the items in this table **** foreach (DataRow row in dt.Rows) { // **** grab our data **** string name = row[ordinalName].ToString(); string comment = row[ordinalComment].ToString(); // **** skip empty rows **** if (string.IsNullOrEmpty(name)) { continue; } // **** skip standard restrictions (not interested right now) **** if (name.Equals(elementBaseName, StringComparison.Ordinal)) { continue; } // **** make this a type **** FhirTypeManager.ProcessSpreadsheetType(name, elementBaseName, comment, isPrimitive, sourceFilename); } }
bool ILanguangeExporter.AppendValueSetOpen(ref StringBuilder sb, string sanitizedAlias, string sanitizedName, ValueSet valueSet) { // **** clear our related objects for the value set **** //_valueSetInterfaceSB.Clear(); _valueSetExportSB.Clear(); string comment; // **** start with a comment **** if (!string.IsNullOrEmpty(valueSet.Description)) { comment = $"/**\n" + $" * {FhirTypeManager.SanitizeComment(valueSet.Description, _lineComment, _indentChar, 1)}\n" + $" */\n"; } else { comment = $"/**\n" + $" * Expanded ValueSet from {valueSet.Url}\n" + $" */\n"; } // **** add our comment **** //_valueSetInterfaceSB.Append(comment); _valueSetExportSB.Append(comment); // **** open our value set (interface has to be before export) **** //_valueSetInterfaceSB.Append($"interface {sanitizedName}_Interface {{\n"); //_valueSetExportSB.Append($"export const {sanitizedName}: {sanitizedName}_Interface = {{\n"); _valueSetExportSB.Append($"export const {sanitizedName} = {{\n"); return(true); }
///------------------------------------------------------------------------------------------------- /// <summary>Writes the TypeScript file</summary> /// /// <remarks>Gino Canessa, 7/31/2019.</remarks> /// /// <param name="options">Options for controlling the operation.</param> ///------------------------------------------------------------------------------------------------- static void WriteTypeScript(Options options) { LanguageTypeScript lang = new LanguageTypeScript(); string filename; // **** check for having an extension **** if (Path.HasExtension(options.OutputFile)) { filename = options.OutputFile; } else { filename = $"{options.OutputFile}.{((ILanguangeExporter)lang).SourceFileExtension}"; } // **** **** Console.WriteLine($"Writing TypeScript file: {filename}"); // **** start our file **** using (StreamWriter writer = new StreamWriter(filename)) { // **** output our data **** FhirTypeManager.OutputForLang( writer, lang, options.TypesToOutput, options.OutputNamespace, options.ExcludeCodes ); writer.Flush(); } }
bool ILanguangeExporter.AppendValueSetCodeConcept( ref StringBuilder sb, string sanitizedValueSetName, string sanitizedCodeName, fhir.CodeSystemConcept concept, string systemUrl ) { // **** start with a comment **** if (!string.IsNullOrEmpty(concept.Definition)) { sb.Append($"\t\t///<summary>{FhirTypeManager.SanitizeComment(concept.Definition, _lineComment, _indentChar, 2)}</summary>\n"); } else if (!string.IsNullOrEmpty(concept.Display)) { sb.Append($"\t\t///<summary>{FhirTypeManager.SanitizeComment(concept.Display, _lineComment, _indentChar, 2)}</summary>\n"); } else { sb.Append($"\t\t///<summary>Value for '{concept.Code}'</summary>\n"); } // **** start our coding **** sb.Append($"\t\tpublic static readonly Coding {sanitizedCodeName} = new Coding\n\t\t{{\n"); sb.Append($"\t\t\tCode = \"{concept.Code}\",\n"); if (!string.IsNullOrEmpty(concept.Display)) { sb.Append($"\t\t\tDisplay = \"{concept.Display.Replace("\"", "\\\"")}\",\n"); } sb.Append($"\t\t\tSystem = \"{systemUrl}\"\n"); sb.Append($"\t\t}};\n"); return(true); }
bool ILanguangeExporter.AppendFhirProperty( ref StringBuilder sb, FhirProperty property, string typeName, bool useLowerCaseName ) { string nameCamel = FhirTypeManager.SanitizeForProperty(property.Name, _reservedWordsSet); string namePascal = FhirTypeManager.SanitizeForProperty(property.NameCapitalized, _reservedWordsSet); string propertyName; string extendedName; if (useLowerCaseName) { propertyName = nameCamel; extendedName = nameCamel; } else { switch (LanguageStyle) { case (int)CSharpStyle.SystemTextJson: propertyName = namePascal; extendedName = namePascal; break; case (int)CSharpStyle.Newtonsoft: propertyName = namePascal; extendedName = namePascal; break; case (int)CSharpStyle.Plain: default: propertyName = namePascal; extendedName = nameCamel; break; } } string propertyAnnotation = ""; string extendedAnnotation = ""; switch (LanguageStyle) { case (int)CSharpStyle.SystemTextJson: propertyAnnotation = $"\t\t[JsonPropertyName(\"{property.Name}\")]\n"; extendedAnnotation = $"\t\t[JsonPropertyName(\"_{property.Name}\")]\n"; break; case (int)CSharpStyle.Newtonsoft: propertyAnnotation = $"\t\t[JsonProperty(PropertyName = \"{property.Name}\")]\n"; extendedAnnotation = $"\t\t[JsonProperty(PropertyName = \"_{property.Name}\")]\n"; break; case (int)CSharpStyle.Plain: default: break; } string comment = FhirTypeManager.SanitizeComment(property.Comment, _lineComment, _indentChar, 2); string optionalFlagString = (_flagOptionals && property.IsOptional) ? "?" : ""; // **** nullable reference types are not allowed in current C# **** switch (typeName) { case "bool": case "decimal": case "DateTime": case "int": case "uint": case "Guid": // **** ignore - types can be optional **** break; default: // **** do not allow reference types to be null (for now) **** optionalFlagString = ""; break; } // **** **** if (property.IsArray) { sb.Append( $"\t\t///<summary>{comment}</summary>\n" + propertyAnnotation + $"\t\tpublic {typeName}[] {propertyName} {{ get; set; }}\n" + $"\t\t///<summary>May contain extended information for property: '{propertyName}'</summary>\n" + extendedAnnotation + $"\t\tpublic Element[] _{extendedName} {{ get; set; }}\n"); return(true); } sb.Append( $"\t\t///<summary>{comment}</summary>\n" + propertyAnnotation + $"\t\tpublic {typeName}{optionalFlagString} {propertyName} {{ get; set; }}\n" + $"\t\t///<summary>May contain extended information for property: '{propertyName}'</summary>\n" + extendedAnnotation + $"\t\tpublic Element _{extendedName} {{ get; set; }}\n"); return(true); }
bool ILanguangeExporter.AppendFhirTypeOpen(ref StringBuilder sb, FhirType fhirType) { string comment = FhirTypeManager.SanitizeComment(fhirType.Comment, _lineComment, _indentChar, 1); if ((fhirType.Properties == null) || (fhirType.Properties.Count == 0)) { //string typeName = string.IsNullOrEmpty(fhirType.TypeName) ? "object" : fhirType.TypeName; sb.Append( $"/**\n" + $" * {comment}\n" + $" * From: {fhirType.SourceFilename}\n" + $" */\n" + $"export type {fhirType.Name} = {fhirType.TypeName};\n" ); return(true); } // **** start with the interface open **** if (string.IsNullOrEmpty(fhirType.TypeName) || fhirType.Name.Equals("Element")) { sb.Append( $"/**\n" + $" * {comment}\n" + $" * From: {fhirType.SourceFilename}\n" + $" */\n" + $"export interface {fhirType.Name} {{\n" ); } else if (fhirType.Name.Equals(fhirType.TypeName, StringComparison.Ordinal)) { sb.Append( $"/**\n" + $" * {comment}\n" + $" * From: {fhirType.SourceFilename}\n" + $" */\n" + $"export interface {fhirType.Name} extends Element {{\n" ); } else { sb.Append( $"/**\n" + $" * {comment}\n" + $" * From: {fhirType.SourceFilename}\n" + $" */\n" + $"export interface {fhirType.Name} extends {fhirType.TypeName} {{\n" ); } // **** output resource type first (if necessary) **** if (FhirTypeManager.DoesTypeRequireResourceTag(fhirType.Name)) { sb.Append( $"\t/** Resource Type Name (for serialization) */\n" + $"\tresourceType: '{fhirType.Name}';\n" ); } return(true); }
static void AddMissingTypes() { // TODO(ginoc): Remove once we find the actual definition of xhtml if (!FhirTypeManager.Exists("xhtml")) { FhirTypeManager.ProcessSpreadsheetType( "xhtml", "string", "WARN: xhtml is defined as string as a definition cannot be found!", true, "definition-file-not-found" ); } // TODO(ginoc): Remove once we find the actual definition of SimpleQuantity if (!FhirTypeManager.Exists("SimpleQuantity")) { FhirTypeManager.ProcessSpreadsheetType( "SimpleQuantity", "Quantity", "WARN: SimpleQuantity definition cannot be found!", false, "definition-file-not-found" ); } // TODO(ginoc): Remove once we find the actual definition of MoneyQuantity if (!FhirTypeManager.Exists("MoneyQuantity")) { FhirTypeManager.ProcessSpreadsheetType( "MoneyQuantity", "Quantity", "WARN: MoneyQuantity definition cannot be found!", false, "definition-file-not-found" ); } // TODO(ginoc): Remove once we find the actual definition of Logical if (!FhirTypeManager.Exists("Logical")) { FhirTypeManager.ProcessSpreadsheetType( "Logical", "Element", "WARN: Logical definition cannot be found!", false, "definition-file-not-found" ); } // TODO(ginoc): Remove once we find the actual definition of Structure (only seen in datatypes/actiondefinition.xml) if (!FhirTypeManager.Exists("Structure")) { FhirTypeManager.ProcessSpreadsheetType( "Structure", "Element", "WARN: Structure definition cannot be found!", false, "definition-file-not-found" ); } }
static bool ProcessPublishedJson(Options options) { string dir = Path.Combine(options.FhirDirectory, "publish"); // **** check for this directory existing **** if (!Directory.Exists(dir)) { Console.WriteLine("Publish directory not found!"); return(false); } // **** get all canonical files in the publish directory **** string[] files = Directory.GetFiles(dir, "*.canonical.json", SearchOption.AllDirectories); // **** traverse the files **** foreach (string filename in files) { // **** read the contents **** string contents = File.ReadAllText(filename); // **** act depending on contents **** if (contents.Contains("\"resourceType\":\"StructureDefinition\"")) { // **** check for ignoring these **** if (options.UseOnlyXmlSpreadsheets) { continue; } // **** parse into an object we can work with **** fhir.StructureDefinition sd = JsonConvert.DeserializeObject <fhir.StructureDefinition>(contents); // **** process this structure definition **** ProcessStructureDefinition(sd, filename); // **** done with this file **** continue; } if (contents.Contains("\"resourceType\":\"CodeSystem\"")) { if (options.ExcludeCodeSystems) { continue; } // **** parse into an object we can work with **** fhir.CodeSystem cs = JsonConvert.DeserializeObject <fhir.CodeSystem>(contents); // **** process this code system **** FhirTypeManager.LoadCodeSystem(cs); // **** done with this file **** continue; } if (contents.Contains("\"resourceType\":\"ValueSet\"")) { if (options.ExcludeValueSets) { continue; } // **** parse into an object we can work with **** fhir.ValueSet vs = JsonConvert.DeserializeObject <fhir.ValueSet>(contents); // **** process this value set **** FhirTypeManager.LoadValueSet(vs, filename); // **** done with this file **** continue; } } // **** success ***** return(true); }
///------------------------------------------------------------------------------------------------- /// <summary>Process the structure type.</summary> /// /// <remarks>Gino Canessa, 8/20/2019.</remarks> /// /// <param name="sd"> The SD.</param> /// <param name="filename">Filename of the file.</param> ///------------------------------------------------------------------------------------------------- static void ProcessStructureType(fhir.StructureDefinition sd, string filename) { // **** figure out if this is a derived type **** string baseType = GetJsonTypeFromStructure(sd); if (string.IsNullOrEmpty(baseType)) { // **** use the base type **** baseType = sd.Type; } // **** reformat circular references **** if (baseType.Equals(sd.Id, StringComparison.Ordinal)) { baseType = ""; } // **** traverse the elements **** for (int elementIndex = 0; elementIndex < sd.Snapshot.Element.Length; elementIndex++) { fhir.ElementDefinition element = sd.Snapshot.Element[elementIndex]; // **** check for initial element (need to change type) **** if (elementIndex == 0) { // **** use the base type **** FhirTypeManager.ProcessSpreadsheetDataElement( sd.Name, baseType, sd.Description, $"{element.Min}..{element.Max}", false, filename ); // **** check for defining a base type **** if (!element.Id.Equals(sd.Name, StringComparison.Ordinal)) { // **** make sure this node exists too **** FhirTypeManager.ProcessSpreadsheetDataElement( element.Id, element.Base.Path, element.Definition, $"{element.Min}..{element.Max}", false, filename ); } // **** no more processing for this entry **** continue; } // **** check for inherited property **** if ((element.Base != null) && (!string.IsNullOrEmpty(element.Base.Path)) && (!element.Base.Path.Equals(element.Id, StringComparison.Ordinal))) { // **** skip this **** continue; } // **** check for type information **** if ((element.Type != null) && (element.Type.Length > 0) && (element.Type[0].Code != null)) { // **** grab the valueSet if present **** string valueSet = ""; if ((element.Binding != null) && (!string.IsNullOrEmpty(element.Binding.ValueSet))) { valueSet = element.Binding.ValueSet; } string cardinality = element.Type.Length > 1 ? "0..1" : $"{element.Min}..{element.Max}"; // **** traverse the types for this element **** foreach (ElementDefinitionType defType in element.Type) { string typeCode = defType.Code; // **** check for the FHIR Type extension **** if ((defType.Extension != null) && (defType.Extension.Length > 0)) { foreach (Extension ext in defType.Extension) { if (ext.Url.Equals( "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", StringComparison.Ordinal)) { typeCode = ext.ValueUrl ?? ext.ValueUri; } } } // **** remove array info from name (if necessary) **** string elementName = element.Path.Replace( "[x]", string.Concat( typeCode.Substring(0, 1).ToUpper(), typeCode.Substring(1) ) ); // **** check for a code type **** if (typeCode == "code") { string[] codeValues = element.Short.Split('|'); // **** process this field **** FhirTypeManager.ProcessSpreadsheetDataElement( elementName, typeCode, (element.Comment == null) ? element.Definition : element.Comment, $"{element.Min}..{element.Max}", false, filename, codeValues ); // **** done with this field **** continue; } // **** process this field **** FhirTypeManager.ProcessSpreadsheetDataElement( elementName, typeCode, element.Definition, cardinality, false, filename, valueSet: valueSet ); } } // **** check for extension type information **** else if ((element.Type != null) && (element.Type.Length > 0) && (element.Type[0].Extension != null)) { // **** find the json type **** string propertyType = ""; foreach (Extension ext in element.Type[0].Extension) { if (ext.Url.EndsWith("fhir-type")) { propertyType = ext.ValueUrl ?? ext.ValueUri; break; } } // **** process this field **** FhirTypeManager.ProcessSpreadsheetDataElement( element.Path, propertyType, element.Definition, $"{element.Min}..{element.Max}", false, filename ); } // **** check for extension type information **** else if ((element.Type != null) && (element.Type.Length > 0) && (element.Type[0]._Code != null)) { // **** find the json type **** string propertyType = ""; foreach (Extension ext in element.Type[0]._Code.Extension) { if (ext.Url.EndsWith("json-type")) { propertyType = ext.ValueString; break; } } // **** process this field **** FhirTypeManager.ProcessSpreadsheetDataElement( element.Path, propertyType, element.Definition, $"{element.Min}..{element.Max}", false, filename ); } // **** use base path **** else { // **** grab the assumed type **** string propertyType = ""; // **** check for override **** if (!string.IsNullOrEmpty(element.ContentReference)) { propertyType = FhirPathToCamelCase(element.ContentReference); } // **** assume base path if nothing else filled out **** if (string.IsNullOrEmpty(propertyType)) { propertyType = FhirPathToCamelCase(element.Base.Path); } // **** process this field **** FhirTypeManager.ProcessSpreadsheetDataElement( element.Path, propertyType, element.Definition, $"{element.Min}..{element.Max}", false, filename ); } } }
///------------------------------------------------------------------------------------------------- /// <summary>Process the fhir directory described by options.</summary> /// /// <remarks>Gino Canessa, 7/15/2019.</remarks> /// /// <param name="options">Options for controlling the operation.</param> ///------------------------------------------------------------------------------------------------- static void ProcessFhirDirectory(Options options) { // **** make sure the directory exists **** if (!Directory.Exists(options.FhirDirectory)) { Console.WriteLine($"Specified FHIR directory: {options.FhirDirectory} not found!"); return; } // **** delete the output file if it exists **** if (File.Exists(options.OutputFile)) { File.Delete(options.OutputFile); } // **** process all of the json files (only once) **** ProcessPublishedJson(options); // **** process XML spreadsheets (if necessary) **** if (options.UseOnlyXmlSpreadsheets) { ProcessXmlSpreadsheets(options); } // **** check for manual spreadsheet overrides **** if ((!options.UseOnlyXmlSpreadsheets) && (!string.IsNullOrEmpty(options.TypesForXmlSpreadsheets))) { ProcessXmlSpreadsheetsFor(options); } // **** expand value sets **** FhirTypeManager.ExpandValueSets(); // **** trim our output to match requested types **** FhirTypeManager.TrimForMatchingNames(options.TypesToOutput); // **** do ResourceType checks **** while (FhirTypeManager.PerformResourceTypeChecks() != 0) { } // **** check for writing typescript **** if (options.LanguageTypeScript) { WriteTypeScript(options); } if (options.LanguageCSharpCore) { WriteCSharp(options, (int)LanguageCSharp.CSharpStyle.SystemTextJson); } if (options.LanguageCSharpPlain) { WriteCSharp(options, (int)LanguageCSharp.CSharpStyle.Plain); } if ((options.LanguageCSharpNewtonsoft) || (options.LanguageCSharpDefault)) { WriteCSharp(options, (int)LanguageCSharp.CSharpStyle.Newtonsoft); } // **** done **** Console.WriteLine("...Done!"); }
bool ILanguangeExporter.AppendFhirTypeOpen(ref StringBuilder sb, FhirType fhirType) { string comment = FhirTypeManager.SanitizeComment(fhirType.Comment, _lineComment, _indentChar, 1); // **** start with the interface open **** if (string.IsNullOrEmpty(fhirType.TypeName) || fhirType.Name.Equals("Element")) { sb.Append( $"\t///<summary>\n" + $"\t///{comment}\n" + $"\t///</summary>\n" + $"\t///<source-file>{fhirType.SourceFilename}</source-file>\n" + $"\tpublic class {fhirType.NameCapitalized}\n" + $"\t{{\n" ); } else if (fhirType.Name.Equals(fhirType.TypeName, StringComparison.Ordinal)) { sb.Append( $"\t///<summary>\n" + $"\t///{comment}\n" + $"\t///</summary>\n" + $"\t///<source-file>{fhirType.SourceFilename}</source-file>\n" + $"\tpublic class {fhirType.NameCapitalized} : Element\n" + $"\t{{\n" ); } else { sb.Append( $"\t///<summary>\n" + $"\t///{comment}\n" + $"\t///</summary>\n" + $"\t///<source-file>{fhirType.SourceFilename}</source-file>\n" + $"\tpublic class {fhirType.NameCapitalized} : {fhirType.TypeName}\n" + $"\t{{\n"); } // **** output resource type first (if necessary) **** if (FhirTypeManager.DoesTypeRequireResourceTag(fhirType.Name)) { // **** add this resource to our list (for polymorphic deserialization) **** _exportedResourceNamesAndTypes.Add(fhirType.Name, fhirType.NameCapitalized); // **** output the correct ResourceType field based on style **** switch (LanguageStyle) { case (int)CSharpStyle.SystemTextJson: sb.Append( $"\t\t///<summary>Resource Type Name (for serialization)</summary>\n" + $"\t\t[JsonPropertyName(\"resourceType\")]\n" + $"\t\tpublic string ResourceType => \"{fhirType.Name}\";\n" ); break; case (int)CSharpStyle.Newtonsoft: sb.Append( $"\t\t///<summary>Resource Type Name (for serialization)</summary>\n" + $"\t\t[JsonProperty(PropertyName = \"resourceType\")]\n" + $"\t\tpublic string ResourceType => \"{fhirType.Name}\";\n" ); break; case (int)CSharpStyle.Plain: default: sb.Append( $"\t\t///<summary>Resource Type Name (for serialization)</summary>\n" + $"\t\tpublic string ResourceType => \"{fhirType.Name}\";\n" ); break; } } return(true); }