Skip to content

Commit b02b2ec

Browse files
authored
Merge pull request #3004 from FirelyTeam/spike/remove-iscopednode
Refactor FP engine against PocoNode
2 parents 930fb98 + 2745a35 commit b02b2ec

File tree

80 files changed

+3050
-2317
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+3050
-2317
lines changed

src/Hl7.Fhir.Base/CompatibilitySuppressions.xml

Lines changed: 703 additions & 129 deletions
Large diffs are not rendered by default.

src/Hl7.Fhir.Base/ElementModel/ElementNodeExtensions.cs

Lines changed: 0 additions & 123 deletions
This file was deleted.

src/Hl7.Fhir.Base/ElementModel/ISourceNode.cs

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,51 @@
88

99
using System.Collections.Generic;
1010

11-
namespace Hl7.Fhir.ElementModel
11+
namespace Hl7.Fhir.ElementModel;
12+
13+
/// <summary>
14+
/// A node within a tree of FHIR data.
15+
/// </summary>
16+
/// <remarks>
17+
/// <para>This interface is typically implemented by a parser for one of the low-level serialization formats for FHIR, i.e.
18+
/// FHIR xml/json/rdf or v3 XML. The interface does not depend on the availability of FHIR metadata and definitions
19+
/// (in contrast to <see cref="ITypedElement" />), so the names of the nodes will have their type suffixes (for choice types)
20+
/// and all primitives values are represented as strings, instead of native objects.</para>
21+
/// <para>Implementations of this interface that want to report errors while parsing should only do so on the
22+
/// <see cref="Children(string)"/> function and <see cref="Text"/> getter.</para>
23+
/// </remarks>
24+
public interface ISourceNode
1225
{
1326
/// <summary>
14-
/// A node within a tree of FHIR data.
27+
/// Gets the name of the node, e.g. "active", "valueQuantity".
1528
/// </summary>
16-
/// <remarks>
17-
/// <para>This interface is typically implemented by a parser for one of the low-level serialization formats for FHIR, i.e.
18-
/// FHIR xml/json/rdf or v3 XML. The interface does not depend on the availability of FHIR metadata and definitions
19-
/// (in contrast to <see cref="ITypedElement" />), so the names of the nodes will have their type suffixes (for choice types)
20-
/// and all primitives values are represented as strings, instead of native objects.</para>
21-
/// <para>Implementations of this interface that want to report errors while parsing should only do so on the
22-
/// <see cref="Children(string)"/> function and <see cref="Text"/> getter.</para>
29+
/// <remarks>Since the node has no type information, choice elements are represented as their
30+
/// name on the wire, possibly including the type suffix for choice elements.
2331
/// </remarks>
24-
public interface ISourceNode
25-
{
26-
/// <summary>
27-
/// Gets the name of the node, e.g. "active", "valueQuantity".
28-
/// </summary>
29-
/// <remarks>Since the node has no type information, choice elements are represented as their
30-
/// name on the wire, possibly including the type suffix for choice elements.
31-
/// </remarks>
32-
string Name { get; }
32+
string Name { get; }
3333

34-
/// <summary>
35-
/// Gets the text of the primitive value of the node
36-
/// </summary>
37-
/// <value>Returns the raw textual value as represented in the serialization, or null if there is no value in this node.</value>
38-
string Text { get; }
34+
/// <summary>
35+
/// Gets the text of the primitive value of the node
36+
/// </summary>
37+
/// <value>Returns the raw textual value as represented in the serialization, or null if there is no value in this node.</value>
38+
string Text { get; }
3939

40-
/// <summary>
41-
/// Gets the location of this node within the tree of data.
42-
/// </summary>
43-
/// <value>A string of dot-separated names representing the path to the node within the tree, including indices
44-
/// to distinguish repeated occurences of an element.</value>
45-
string Location { get; }
40+
/// <summary>
41+
/// Gets the location of this node within the tree of data.
42+
/// </summary>
43+
/// <value>A string of dot-separated names representing the path to the node within the tree, including indices
44+
/// to distinguish repeated occurences of an element.</value>
45+
string Location { get; }
4646

47-
/// <summary>
48-
/// Enumerates the direct child nodes of the current node (if any).
49-
/// </summary>
50-
/// <param name="name">Optional. The name filter for the children. Can be omitted to not filter by name.</param>
51-
/// <returns>The children of the node matching the given filter, or all children if no filter was specified.
52-
/// If no children match the given filter, the function returns an empty enumerable.</returns>
53-
/// <remarks>
54-
/// <para>If the <paramref name="name"/>parameter ends in an asterix ('*'),
55-
/// the function will return the children of which the name starts with the given name.</para>
56-
/// <para>Repeating elements will always be returned consecutively.</para></remarks>
57-
IEnumerable<ISourceNode> Children(string name = null);
58-
}
47+
/// <summary>
48+
/// Enumerates the direct child nodes of the current node (if any).
49+
/// </summary>
50+
/// <param name="name">Optional. The name filter for the children. Can be omitted to not filter by name.</param>
51+
/// <returns>The children of the node matching the given filter, or all children if no filter was specified.
52+
/// If no children match the given filter, the function returns an empty enumerable.</returns>
53+
/// <remarks>
54+
/// <para>If the <paramref name="name"/>parameter ends in an asterix ('*'),
55+
/// the function will return the children of which the name starts with the given name.</para>
56+
/// <para>Repeating elements will always be returned consecutively.</para></remarks>
57+
IEnumerable<ISourceNode> Children(string name = null);
5958
}

src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ private ClassMapping classMappingForElement(ITypedElement node, PropertyMapping?
141141

142142
// Otherwise, let's use the ITypedElement's instance type.
143143
if (node.InstanceType is { } instanceType &&
144-
inspector.FindClassMapping(instanceType) is { NativeType.IsAbstract: false } mapping)
144+
inspector.FindClassMapping(instanceType) is { NativeType.IsAbstract: false } mapping && typeof(Base).IsAssignableFrom(mapping.NativeType))
145145
return mapping;
146146

147147
// No useable concrete type in the property, nor in the instance type, so we need to create
@@ -183,7 +183,7 @@ private ClassMapping determineBestDynamicMappingForElement(ITypedElement node)
183183
if (node.Value is not null || (node.InstanceType is { } it && char.IsLower(it[0])))
184184
return determineBestPrimitiveMapping();
185185

186-
if (node.Annotation<IResourceTypeSupplier>() is not null)
186+
if (node.Annotation<IResourceTypeSupplier>() is not null || node.Definition?.IsResource is true)
187187
return getClassMapping(DYNAMIC_RESOURCE_TYPE_NAME);
188188

189189
return getClassMapping(DYNAMIC_DATATYPE_TYPE_NAME);

src/Hl7.Fhir.Base/ElementModel/PocoBuilderExtensions.cs

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using Hl7.Fhir.Model;
2+
using Hl7.FhirPath;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
7+
#nullable enable
8+
9+
namespace Hl7.Fhir.ElementModel;
10+
11+
public partial record PocoNode
12+
{
13+
/// <summary>
14+
/// Constructs a PocoNode from a PrimitiveType
15+
/// </summary>
16+
/// <returns></returns>
17+
public static PocoNode ForPrimitive(PrimitiveType primitive) =>
18+
new PrimitiveNode(primitive, null, null);
19+
20+
21+
/// <summary>
22+
/// Constructs a PocoNode from an object. Allowed objects are those that can be converted to a PrimitiveType, and are not yet PrimitiveTypes.
23+
/// </summary>
24+
/// <returns></returns>
25+
public static PocoNode ForAnyPrimitive(object value)
26+
{
27+
return ForPrimitive(PrimitiveNode.InferFromValue(value));
28+
}
29+
30+
/// <summary>
31+
/// Constructs a PocoNode from a value and a type. The type must be a PrimitiveType.
32+
/// </summary>
33+
/// <param name="value"></param>
34+
/// <typeparam name="T"></typeparam>
35+
/// <returns></returns>
36+
public static PocoNode ForPrimitive<T>(object value) where T : PrimitiveType, new() =>
37+
new PrimitiveNode(new T { ObjectValue = value }, null, null);
38+
39+
/// <summary>
40+
/// Constructs a PocoNode from a list of PrimitiveTypes
41+
/// </summary>
42+
/// <param name="primitives"></param>
43+
/// <param name="name"></param>
44+
/// <returns></returns>
45+
public static IEnumerable<PocoNode> FromList(IEnumerable<PrimitiveType> primitives, string? name = null) =>
46+
primitives.Select(ForPrimitive);
47+
48+
/// <summary>
49+
/// Constructs multiple PocoNodes from a list of values and a type. The type must be a PrimitiveType.
50+
/// </summary>
51+
/// <param name="values"></param>
52+
/// <typeparam name="T"></typeparam>
53+
/// <returns></returns>
54+
public static IEnumerable<PocoNode> FromList<T>(IEnumerable<object> values) where T : PrimitiveType, new() =>
55+
values.Select(ForPrimitive<T>);
56+
57+
/// <summary>
58+
/// Constructs multiple PocoNodes from a list of objects. Allowed objects are those that can be converted to a PrimitiveType, and are not yet PrimitiveTypes.
59+
/// </summary>
60+
/// <param name="values"></param>
61+
/// <returns></returns>
62+
public static IEnumerable<PocoNode> FromAnyList(IEnumerable<object> values) =>
63+
values.Select(v => v as PocoNode ?? ForAnyPrimitive(v));
64+
}
65+
66+
public record PrimitiveNode(PrimitiveType Primitive, PocoNodeOrList? ParentNode, int? Index, string? Name = null) : PocoNode(Primitive, ParentNode, Index, Name)
67+
{
68+
protected override object? ValueInternal => Primitive.ToITypedElementValue();
69+
internal object? Value => ValueInternal;
70+
71+
internal static PrimitiveType InferFromValue(object value) => value switch
72+
{
73+
Types.Quantity qt => new FPQuantity(qt),
74+
Types.DateTime dt => new FPDateTime(dt),
75+
Types.Date d => new FPDate(d),
76+
Types.Time t => new FPTime(t),
77+
decimal dec => new FPDecimal(dec),
78+
float f => new FPDecimal((decimal)f),
79+
double d => new FPDecimal((decimal)d),
80+
bool b => new FPBoolean(b),
81+
int i => new FPInteger(i),
82+
long l => new FPLong(l),
83+
string s => new FPString(s),
84+
_ => throw new ArgumentException("Cannot infer primitive type from value", nameof(value))
85+
};
86+
87+
protected override string? TextInternal => Primitive.ToString();
88+
}
89+
90+
internal record PrimitiveListNode(IReadOnlyList<PrimitiveType> Primitives, PocoNodeOrList? ParentNode, string? Name = null) : PocoListNode(Primitives, ParentNode, Name ?? "value")
91+
{
92+
public override IEnumerator<PocoNode> GetEnumerator() =>
93+
Primitives.Select((primitive, index) => new PrimitiveNode(primitive, ParentNode, index, Name)).GetEnumerator();
94+
95+
internal IEnumerable<object?> Values => Primitives.Select(p => p.ObjectValue);
96+
}

0 commit comments

Comments
 (0)