Skip to content

Commit

Permalink
Merge pull request #2913 from FirelyTeam/spike/make-resource-implemen…
Browse files Browse the repository at this point in the history
…t-iscopednode

#2910 Make Base implement IScopedNode - phase 1
  • Loading branch information
ewoutkramer authored Oct 22, 2024
2 parents 283ed30 + 95a0b18 commit f36ad42
Show file tree
Hide file tree
Showing 27 changed files with 692 additions and 170 deletions.
322 changes: 322 additions & 0 deletions src/Hl7.Fhir.Base/CompatibilitySuppressions.xml

Large diffs are not rendered by default.

70 changes: 0 additions & 70 deletions src/Hl7.Fhir.Base/ElementModel/IBaseElementNavigator.cs

This file was deleted.

45 changes: 44 additions & 1 deletion src/Hl7.Fhir.Base/ElementModel/ITypedElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

using Hl7.Fhir.Specification;
using System.Collections.Generic;

#nullable enable

Expand All @@ -22,10 +23,52 @@ namespace Hl7.Fhir.ElementModel
/// </remarks>

#pragma warning disable CS0618 // Type or member is obsolete
public interface ITypedElement : IBaseElementNavigator<ITypedElement>
public interface ITypedElement
#pragma warning restore CS0618 // Type or member is obsolete
{
/// <summary>
/// Enumerate the child nodes present in the source representation (if any)
/// </summary>
/// <param name="name">Return only the children with the given name.</param>
/// <returns></returns>
IEnumerable<ITypedElement> Children(string? name = null);

/// <summary>
/// Name of the node, e.g. "active", "value".
/// </summary>
string Name { get; }

/// <summary>
/// Type of the node. If a FHIR type, this is just a simple string, otherwise a StructureDefinition url for a type defined as a logical model.
/// </summary>
string? InstanceType { get; }

/// <summary>
/// The value of the node (if it represents a primitive FHIR value)
/// </summary>
/// <remarks>
/// FHIR primitives are mapped to underlying C# types as follows:
///
/// instant Hl7.Fhir.ElementModel.Types.DateTime
/// time Hl7.Fhir.ElementModel.Types.Time
/// date Hl7.Fhir.ElementModel.Types.Date
/// dateTime Hl7.Fhir.ElementModel.Types.DateTime
/// decimal decimal
/// boolean bool
/// integer int
/// unsignedInt int
/// positiveInt int
/// long/integer64 long (name will be finalized in R5)
/// string string
/// code string
/// id string
/// uri, oid, uuid,
/// canonical, url string
/// markdown string
/// base64Binary string (uuencoded)
/// xhtml string
/// </remarks>
object? Value { get; }

/// <summary>
/// An indication of the location of this node within the data represented by the <c>ITypedElement</c>.
Expand Down
63 changes: 21 additions & 42 deletions src/Hl7.Fhir.Base/ElementModel/PocoElementNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using P = Hl7.Fhir.ElementModel.Types;

namespace Hl7.Fhir.ElementModel
Expand All @@ -32,7 +31,7 @@ internal PocoElementNode(ModelInspector inspector, Base root, string rootName =
{
Current = root;
_inspector = inspector;
_myClassMapping = _inspector.FindOrImportClassMapping(root.GetType());
_myClassMapping = _inspector.FindOrImportClassMapping(root.GetType())!;

InstanceType = ((IStructureDefinitionSummary)_myClassMapping).TypeName;
Definition = ElementDefinitionSummary.ForRoot(_myClassMapping, rootName ?? root.TypeName);
Expand Down Expand Up @@ -70,7 +69,7 @@ private Type determineInstanceType(PropertyMapping definition)
{
"url" => typeof(FhirUri),
"id" => typeof(FhirString),
"div" => typeof(XHtml),
//"div" => typeof(XHtml),
_ => throw new NotSupportedException(
$"Encountered unexpected primitive type {Name} in backward compat behaviour for PocoElementNode.InstanceType.")
};
Expand Down Expand Up @@ -205,39 +204,20 @@ internal object ToITypedElementValue()
{
try
{
switch (Current)
return Current switch
{
case Hl7.Fhir.Model.Instant ins when ins.Value.HasValue:
return P.DateTime.FromDateTimeOffset(ins.Value.Value);
case Hl7.Fhir.Model.Time time when time.Value is { }:
return P.Time.Parse(time.Value);
case Hl7.Fhir.Model.Date dt when dt.Value is { }:
return P.Date.Parse(dt.Value);
case FhirDateTime fdt when fdt.Value is { }:
return P.DateTime.Parse(fdt.Value);
case Hl7.Fhir.Model.Integer fint:
if (!fint.Value.HasValue)
return null;
return (int)fint.Value;
case Hl7.Fhir.Model.Integer64 fint64:
if (!fint64.Value.HasValue)
return null;
return (long)fint64.Value;
case Hl7.Fhir.Model.PositiveInt pint:
if (!pint.Value.HasValue)
return null;
return (int)pint.Value;
case Hl7.Fhir.Model.UnsignedInt unsint:
if (!unsint.Value.HasValue)
return null;
return (int)unsint.Value;
case Hl7.Fhir.Model.Base64Binary b64:
return b64.Value != null ? PrimitiveTypeConverter.ConvertTo<string>(b64.Value) : null;
case PrimitiveType prim:
return prim.ObjectValue;
default:
return null;
}
Instant { Value: { } ins } => P.DateTime.FromDateTimeOffset(ins),
Time { Value: { } time } => P.Time.Parse(time),
Date { Value: { } dt } => P.Date.Parse(dt),
FhirDateTime { Value: { } fdt } => P.DateTime.Parse(fdt),
Integer fint => fint.Value,
Integer64 fint64 => fint64.Value,
PositiveInt pint => pint.Value,
UnsignedInt unsint => unsint.Value,
Base64Binary { Value: { } b64 } => PrimitiveTypeConverter.ConvertTo<string>(b64),
PrimitiveType prim => prim.ObjectValue,
_ => null
};
}
catch (FormatException)
{
Expand All @@ -247,25 +227,24 @@ internal object ToITypedElementValue()
}


public string InstanceType { get; private set; }
public string InstanceType { get; }

public string Location { get; private set; }
public string Location { get; }

public string ResourceType => Current is Resource ? InstanceType : null;

public IEnumerable<object> Annotations(Type type)
{
if (type == typeof(PocoElementNode) || type == typeof(ITypedElement) || type == typeof(IShortPathGenerator))
return new[] { this };
return [this];
else if (type == typeof(IFhirValueProvider))
return new[] { this };
return [this];
else if (type == typeof(IResourceTypeSupplier))
return new[] { this };
return [this];
else if (FhirValue is IAnnotated ia)
return ia.Annotations(type);

else
return Enumerable.Empty<object>();
return [];
}
}
}
8 changes: 5 additions & 3 deletions src/Hl7.Fhir.Base/ElementModel/TypedElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Hl7.Fhir.Model;
using System;
using System.Linq;
using System.Runtime.CompilerServices;

namespace Hl7.Fhir.ElementModel
{
Expand All @@ -26,6 +27,7 @@ public static class TypedElementExtensions
/// <param name="modelInspector">The <see cref="ModelInspector"/> containing the POCO classes to be used for deserialization.</param>
/// <param name="rootName"></param>
/// <returns></returns>
[TemporarilyChanged]
public static ITypedElement ToTypedElement(this Base @base, ModelInspector modelInspector, string? rootName = null)
=> new PocoElementNode(modelInspector, @base, rootName: rootName);

Expand All @@ -39,7 +41,7 @@ public static ITypedElement ToTypedElement(this Base @base, ModelInspector model
/// of the equation.</param>
/// <returns><c>true</c> when the ITypedElements are equal, <c>false</c> otherwise.</returns>
#pragma warning disable CS0618 // Type or member is obsolete
public static bool IsExactlyEqualTo<T>(this T? left, T? right, bool ignoreOrder = false) where T : IBaseElementNavigator<T>
public static bool IsExactlyEqualTo(this ITypedElement? left, ITypedElement? right, bool ignoreOrder = false)
#pragma warning restore CS0618 // Type or member is obsolete
{
if (left == null && right == null) return true;
Expand Down Expand Up @@ -102,7 +104,7 @@ public static bool ValueEquality<T1, T2>(T1? val1, T2? val2)
/// <param name="pattern"></param>
/// <returns><c>true</c> when <paramref name="value"/> matches the <paramref name="pattern"/>, <c>false</c> otherwise.</returns>
#pragma warning disable CS0618 // Type or member is obsolete
public static bool Matches<T>(this T value, T pattern) where T : IBaseElementNavigator<T>
public static bool Matches(this ITypedElement value, ITypedElement pattern)
#pragma warning restore CS0618 // Type or member is obsolete
{
if (value == null && pattern == null) return true;
Expand All @@ -120,4 +122,4 @@ public static bool Matches<T>(this T value, T pattern) where T : IBaseElementNav
}
}
}
#nullable restore
#nullable restore
40 changes: 17 additions & 23 deletions src/Hl7.Fhir.Base/ElementModel/TypedElementParseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@ public static class TypedElementParseExtensions
#pragma warning restore CS0618 // Type or member is obsolete

/// <inheritdoc cref="ParseBindable"/>
[Obsolete("WARNING! Intended for internal API usage exclusively, interface IBaseElementNavigator can be changed in " +
"the near future.")]
public static Element? ParseBindableInternal<T>(this IBaseElementNavigator<T> instance) where T : IBaseElementNavigator<T>
[Obsolete("WARNING! Intended for internal API usage exclusively")]
public static Element? ParseBindableInternal(this ITypedElement instance)
{
return instance.InstanceType switch
{
FhirTypeConstants.CODE => instance.ParsePrimitiveInternal<Code, T>(),
FhirTypeConstants.STRING => new Code(instance.ParsePrimitiveInternal<FhirString, T>().Value),
FhirTypeConstants.URI => new Code(instance.ParsePrimitiveInternal<FhirUri, T>().Value),
FhirTypeConstants.CODE => instance.ParsePrimitiveInternal<Code>(),
FhirTypeConstants.STRING => new Code(instance.ParsePrimitiveInternal<FhirString>().Value),
FhirTypeConstants.URI => new Code(instance.ParsePrimitiveInternal<FhirUri>().Value),
FhirTypeConstants.CODING => instance.ParseCodingInternal(),
FhirTypeConstants.CODEABLE_CONCEPT => instance.ParseCodeableConceptInternal(),
FhirTypeConstants.QUANTITY => parseQuantity(),
Expand Down Expand Up @@ -88,9 +87,8 @@ public static Quantity ParseQuantity(this ITypedElement instance)
=> ParseQuantityInternal(instance);
#pragma warning restore CS0618 // Type or member is obsolete

[Obsolete("WARNING! Intended for internal API usage exclusively, interface IBaseElementNavigator can be changed in " +
"the near future.")]
public static Quantity ParseQuantityInternal<T>(this IBaseElementNavigator<T> instance) where T : IBaseElementNavigator<T>
[Obsolete("WARNING! Intended for internal API usage exclusively")]
public static Quantity ParseQuantityInternal(this ITypedElement instance)
{
var newQuantity = new Quantity
{
Expand All @@ -111,12 +109,11 @@ public static Quantity ParseQuantityInternal<T>(this IBaseElementNavigator<T> in
#region ParsePrimitive
public static T ParsePrimitive<T>(this ITypedElement instance) where T : PrimitiveType, new()
#pragma warning disable CS0618 // Type or member is obsolete
=> ParsePrimitiveInternal<T, ITypedElement>(instance);
=> ParsePrimitiveInternal<T>(instance);
#pragma warning restore CS0618 // Type or member is obsolete

[Obsolete("WARNING! Intended for internal API usage exclusively, interface IBaseElementNavigator can be changed in " +
"the near future.")]
public static T ParsePrimitiveInternal<T, U>(this IBaseElementNavigator<U> instance) where T : PrimitiveType, new() where U : IBaseElementNavigator<U>
[Obsolete("WARNING! Intended for internal API usage exclusively")]
public static T ParsePrimitiveInternal<T>(this ITypedElement instance) where T : PrimitiveType, new()
=> new() { ObjectValue = instance.Value };

#endregion
Expand All @@ -128,9 +125,8 @@ public static Coding ParseCoding(this ITypedElement instance)
#pragma warning restore CS0618 // Type or member is obsolete


[Obsolete("WARNING! Intended for internal API usage exclusively, interface IBaseElementNavigator can be changed in " +
"the near future.")]
public static Coding ParseCodingInternal<T>(this IBaseElementNavigator<T> instance) where T : IBaseElementNavigator<T>
[Obsolete("WARNING! Intended for internal API usage exclusively")]
public static Coding ParseCodingInternal(this ITypedElement instance)
{
return new Coding()
{
Expand All @@ -149,9 +145,8 @@ public static ResourceReference ParseResourceReference(this ITypedElement instan
=> instance.ParseResourceReferenceInternal();
#pragma warning restore CS0618 // Type or member is obsolete

[Obsolete("WARNING! Intended for internal API usage exclusively, interface IBaseElementNavigator can be changed in " +
"the near future.")]
public static ResourceReference ParseResourceReferenceInternal<T>(this IBaseElementNavigator<T> instance) where T : IBaseElementNavigator<T>
[Obsolete("WARNING! Intended for internal API usage exclusively")]
public static ResourceReference ParseResourceReferenceInternal(this ITypedElement instance)
{
return new ResourceReference()
{
Expand All @@ -167,9 +162,8 @@ public static CodeableConcept ParseCodeableConcept(this ITypedElement instance)
=> instance.ParseCodeableConceptInternal();
#pragma warning restore CS0618 // Type or member is obsolete

[Obsolete("WARNING! Intended for internal API usage exclusively, interface IBaseElementNavigator can be changed in " +
"the near future.")]
public static CodeableConcept ParseCodeableConceptInternal<T>(this IBaseElementNavigator<T> instance) where T : IBaseElementNavigator<T>
[Obsolete("WARNING! Intended for internal API usage exclusively")]
public static CodeableConcept ParseCodeableConceptInternal(this ITypedElement instance)
{
return new CodeableConcept()
{
Expand All @@ -181,7 +175,7 @@ public static CodeableConcept ParseCodeableConceptInternal<T>(this IBaseElementN
#endregion

#pragma warning disable CS0618 // Type or member is obsolete
public static string? GetString<T>(this IEnumerable<T> instance) where T : IBaseElementNavigator<T>
public static string? GetString(this IEnumerable<ITypedElement> instance)
#pragma warning restore CS0618 // Type or member is obsolete
=> instance.SingleOrDefault()?.Value as string;
}
Expand Down
3 changes: 0 additions & 3 deletions src/Hl7.Fhir.Base/FhirPath/Expressions/Closure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

using Hl7.Fhir.ElementModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;

namespace Hl7.FhirPath.Expressions
{
Expand Down
Loading

0 comments on commit f36ad42

Please sign in to comment.